@fc-components/monaco-editor 0.1.27 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/expr/__tests__/__mocks__/monaco-editor.d.ts +28 -0
- package/dist/expr/completion/getCompletionProvider.d.ts +4 -0
- package/dist/expr/expr.d.ts +83 -0
- package/dist/expr/index.d.ts +3 -0
- package/dist/expr/parser/index.d.ts +3 -0
- package/dist/expr/parser/lexer.d.ts +27 -0
- package/dist/expr/parser/parser.d.ts +66 -0
- package/dist/expr/parser/types.d.ts +32 -0
- package/dist/expr/types.d.ts +17 -0
- package/dist/expr/validation.d.ts +12 -0
- package/dist/index.d.ts +2 -0
- package/dist/monaco-editor.cjs.development.js +1986 -3
- package/dist/monaco-editor.cjs.development.js.map +1 -1
- package/dist/monaco-editor.cjs.production.min.js +1 -1
- package/dist/monaco-editor.cjs.production.min.js.map +1 -1
- package/dist/monaco-editor.esm.js +1989 -7
- package/dist/monaco-editor.esm.js.map +1 -1
- package/dist/promql/completion/situation.d.ts +2 -0
- package/package.json +6 -2
- package/src/expr/__tests__/__mocks__/monaco-editor.ts +34 -0
- package/src/expr/__tests__/expr.test.tsx +339 -0
- package/src/expr/completion/getCompletionProvider.ts +133 -0
- package/src/expr/expr.ts +229 -0
- package/src/expr/index.tsx +322 -0
- package/src/expr/parser/index.ts +3 -0
- package/src/expr/parser/lexer.ts +377 -0
- package/src/expr/parser/parser.ts +581 -0
- package/src/expr/parser/types.ts +77 -0
- package/src/expr/types.ts +17 -0
- package/src/expr/validation.ts +209 -0
- package/src/index.tsx +2 -0
- package/src/promql/__tests__/completions.test.ts +72 -0
- package/src/promql/__tests__/situation.test.ts +85 -0
- package/src/promql/completion/completions.ts +11 -2
- package/src/promql/completion/situation.ts +65 -1
- package/src/promql/promql.ts +3 -1
|
@@ -3,7 +3,7 @@ import MonacoEditor from 'react-monaco-editor';
|
|
|
3
3
|
import * as monaco from 'monaco-editor';
|
|
4
4
|
import { languages, Range, editor, Selection, KeyMod, KeyCode, MarkerSeverity } from 'monaco-editor';
|
|
5
5
|
import { promLanguageDefinition } from 'monaco-promql';
|
|
6
|
-
import { parser, LabelName, UnquotedLabelMatcher, LabelMatchers, VectorSelector, PromQL, Identifier, FunctionCallBody, StringLiteral, BinaryExpr, NumberDurationLiteralInDurationContext, MatrixSelector, GroupingLabels, AggregateModifier, AggregateExpr, MatchOp, EqlSingle, EqlRegex, Neq, NeqRegex } from '@fc-components/lezer-metricsql';
|
|
6
|
+
import { parser as parser$1, LabelName, UnquotedLabelMatcher, LabelMatchers, VectorSelector, PromQL, Identifier, FunctionCallBody, StringLiteral, BinaryExpr, NumberDurationLiteralInDurationContext, MatrixSelector, GroupingLabels, WithExpr, WithAssignment, AggregateModifier, AggregateExpr, MatchOp, EqlSingle, EqlRegex, Neq, NeqRegex } from '@fc-components/lezer-metricsql';
|
|
7
7
|
import { v4 } from 'uuid';
|
|
8
8
|
import { css } from '@emotion/css';
|
|
9
9
|
|
|
@@ -340,6 +340,7 @@ for (var _i = 0, aggregations_1 = aggregations; _i < aggregations_1.length; _i++
|
|
|
340
340
|
// PromQL vector matching + the by and without clauses
|
|
341
341
|
// (https://prometheus.io/docs/prometheus/latest/querying/operators/#vector-matching)
|
|
342
342
|
var vectorMatching = ['on', 'ignoring', 'group_right', 'group_left', 'by', 'without'];
|
|
343
|
+
var cteKeywords = ['with'];
|
|
343
344
|
// Produce a regex matching elements : (elt1|elt2|...)
|
|
344
345
|
var vectorMatchingRegex = '(' + /*#__PURE__*/vectorMatching.reduce(function (prev, curr) {
|
|
345
346
|
return prev + '|' + curr;
|
|
@@ -351,7 +352,7 @@ var operators = ['+', '-', '*', '/', '%', '^', '==', '!=', '>', '<', '>=', '<=',
|
|
|
351
352
|
// (https://prometheus.io/docs/prometheus/latest/querying/basics/#offset-modifier)
|
|
352
353
|
var offsetModifier = ['offset'];
|
|
353
354
|
// Merging all the keywords in one list
|
|
354
|
-
var keywords = /*#__PURE__*/aggregations.concat(functions).concat(aggregationsOverTime).concat(vectorMatching).concat(offsetModifier);
|
|
355
|
+
var keywords = /*#__PURE__*/aggregations.concat(functions).concat(aggregationsOverTime).concat(vectorMatching).concat(offsetModifier).concat(cteKeywords);
|
|
355
356
|
// noinspection JSUnusedGlobalSymbols
|
|
356
357
|
var language = {
|
|
357
358
|
ignoreCase: false,
|
|
@@ -1289,6 +1290,15 @@ var RESOLVERS = [{
|
|
|
1289
1290
|
}, {
|
|
1290
1291
|
path: [GroupingLabels],
|
|
1291
1292
|
fun: resolveLabelsForGrouping
|
|
1293
|
+
}, {
|
|
1294
|
+
path: [WithExpr],
|
|
1295
|
+
fun: resolveWithExpr
|
|
1296
|
+
}, {
|
|
1297
|
+
path: [WithExpr, PromQL],
|
|
1298
|
+
fun: resolveWithExpr
|
|
1299
|
+
}, {
|
|
1300
|
+
path: [WithAssignment, WithExpr],
|
|
1301
|
+
fun: resolveWithExpr
|
|
1292
1302
|
}];
|
|
1293
1303
|
var LABEL_OP_MAP = /*#__PURE__*/new Map([[EqlSingle, '='], [EqlRegex, '=~'], [Neq, '!='], [NeqRegex, '!~']]);
|
|
1294
1304
|
function getLabelOp(opNode) {
|
|
@@ -1461,6 +1471,25 @@ function resolveInFunction() {
|
|
|
1461
1471
|
type: 'IN_FUNCTION'
|
|
1462
1472
|
};
|
|
1463
1473
|
}
|
|
1474
|
+
function resolveWithExpr(node, text, pos) {
|
|
1475
|
+
// Find the containing WithExpr node (node may be a WithAssignment child)
|
|
1476
|
+
var withExprNode = node.type.id === WithExpr ? node : node.parent;
|
|
1477
|
+
while (withExprNode && withExprNode.type.id !== WithExpr) {
|
|
1478
|
+
withExprNode = withExprNode.parent;
|
|
1479
|
+
}
|
|
1480
|
+
if (!withExprNode) return null;
|
|
1481
|
+
// Only return IN_WITH_BODY when cursor is in the body expression (after the closing ')')
|
|
1482
|
+
var children = getNodeChildren(withExprNode);
|
|
1483
|
+
var closeParen = children.find(function (c) {
|
|
1484
|
+
return getNodeText(c, text) === ')';
|
|
1485
|
+
});
|
|
1486
|
+
if (closeParen && pos > closeParen.to) {
|
|
1487
|
+
return {
|
|
1488
|
+
type: 'IN_WITH_BODY'
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1464
1493
|
function resolveDurations() {
|
|
1465
1494
|
return {
|
|
1466
1495
|
type: 'IN_DURATION'
|
|
@@ -1570,6 +1599,25 @@ function getErrorNode(tree, pos) {
|
|
|
1570
1599
|
}
|
|
1571
1600
|
return null;
|
|
1572
1601
|
}
|
|
1602
|
+
function findMatchingParen(text, openPos) {
|
|
1603
|
+
var depth = 0;
|
|
1604
|
+
for (var i = openPos; i < text.length; i++) {
|
|
1605
|
+
if (text[i] === '(') depth++;
|
|
1606
|
+
if (text[i] === ')') {
|
|
1607
|
+
depth--;
|
|
1608
|
+
if (depth === 0) return i;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
return -1;
|
|
1612
|
+
}
|
|
1613
|
+
function isInWithBody(text, pos) {
|
|
1614
|
+
var withMatch = text.match(/^with\s*\(/i);
|
|
1615
|
+
if (!withMatch) return false;
|
|
1616
|
+
var openParenPos = withMatch[0].length - 1;
|
|
1617
|
+
var closeParenPos = findMatchingParen(text, openParenPos);
|
|
1618
|
+
if (closeParenPos === -1) return false;
|
|
1619
|
+
return pos > closeParenPos;
|
|
1620
|
+
}
|
|
1573
1621
|
function getSituation(text, pos) {
|
|
1574
1622
|
// there is a special-case when we are at the start of writing text,
|
|
1575
1623
|
// so we handle that case first
|
|
@@ -1578,13 +1626,19 @@ function getSituation(text, pos) {
|
|
|
1578
1626
|
type: 'EMPTY'
|
|
1579
1627
|
};
|
|
1580
1628
|
}
|
|
1629
|
+
// text-based check for with body (fallback until grammar supports WithExpr)
|
|
1630
|
+
if (isInWithBody(text, pos)) {
|
|
1631
|
+
return {
|
|
1632
|
+
type: 'IN_WITH_BODY'
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1581
1635
|
/**
|
|
1582
1636
|
PromQL
|
|
1583
1637
|
Expr
|
|
1584
1638
|
VectorSelector
|
|
1585
1639
|
LabelMatchers
|
|
1586
1640
|
*/
|
|
1587
|
-
var tree = parser.parse(text);
|
|
1641
|
+
var tree = parser$1.parse(text);
|
|
1588
1642
|
// if the tree contains error, it is very probable that
|
|
1589
1643
|
// our node is one of those error-nodes.
|
|
1590
1644
|
// also, if there are errors, the node lezer finds us,
|
|
@@ -1660,12 +1714,19 @@ function _getAllFunctionsAndMetricNamesCompletions() {
|
|
|
1660
1714
|
while (1) switch (_context.n) {
|
|
1661
1715
|
case 0:
|
|
1662
1716
|
metricNames = getAllMetricNamesCompletions(dataProvider);
|
|
1663
|
-
return _context.a(2, [].concat(FUNCTION_COMPLETIONS, metricNames));
|
|
1717
|
+
return _context.a(2, [CTE_KEYWORD_COMPLETION].concat(FUNCTION_COMPLETIONS, metricNames));
|
|
1664
1718
|
}
|
|
1665
1719
|
}, _callee);
|
|
1666
1720
|
}));
|
|
1667
1721
|
return _getAllFunctionsAndMetricNamesCompletions.apply(this, arguments);
|
|
1668
1722
|
}
|
|
1723
|
+
var CTE_KEYWORD_COMPLETION = {
|
|
1724
|
+
type: 'FUNCTION',
|
|
1725
|
+
label: 'with',
|
|
1726
|
+
insertText: 'with (',
|
|
1727
|
+
detail: 'with (cte_name = expr, ...) expr',
|
|
1728
|
+
documentation: 'Define Common Table Expressions (CTEs) for use in the query. MetricsQL extension.'
|
|
1729
|
+
};
|
|
1669
1730
|
var DURATION_COMPLETIONS = /*#__PURE__*/['1m', '5m', '10m', '30m', '1h', '1d'].map(function (text) {
|
|
1670
1731
|
return {
|
|
1671
1732
|
type: 'DURATION',
|
|
@@ -1907,6 +1968,7 @@ function getCompletions(situation, dataProvider) {
|
|
|
1907
1968
|
return Promise.resolve(DURATION_COMPLETIONS);
|
|
1908
1969
|
case 'IN_FUNCTION':
|
|
1909
1970
|
return getAllFunctionsAndMetricNamesCompletions(dataProvider);
|
|
1971
|
+
case 'IN_WITH_BODY':
|
|
1910
1972
|
case 'AT_ROOT':
|
|
1911
1973
|
{
|
|
1912
1974
|
return getAllFunctionsAndMetricNamesCompletions(dataProvider);
|
|
@@ -1915,7 +1977,7 @@ function getCompletions(situation, dataProvider) {
|
|
|
1915
1977
|
{
|
|
1916
1978
|
var metricNames = getAllMetricNamesCompletions(dataProvider);
|
|
1917
1979
|
var historyCompletions = getAllHistoryCompletions();
|
|
1918
|
-
return Promise.resolve([].concat(historyCompletions, FUNCTION_COMPLETIONS, metricNames));
|
|
1980
|
+
return Promise.resolve([].concat(historyCompletions, [CTE_KEYWORD_COMPLETION], FUNCTION_COMPLETIONS, metricNames));
|
|
1919
1981
|
}
|
|
1920
1982
|
case 'IN_LABEL_SELECTOR_NO_LABEL_NAME':
|
|
1921
1983
|
return getLabelNamesForSelectorCompletions(situation.metricName, situation.hasOperator, situation.otherLabels, dataProvider);
|
|
@@ -2246,7 +2308,7 @@ function PromQLEditor(props) {
|
|
|
2246
2308
|
var model = editor$1.getModel();
|
|
2247
2309
|
if (model) {
|
|
2248
2310
|
var query = model.getValue();
|
|
2249
|
-
var errors = validateQuery(query, interpolateString ? interpolateString(query) : query, model.getLinesContent(), parser) || [];
|
|
2311
|
+
var errors = validateQuery(query, interpolateString ? interpolateString(query) : query, model.getLinesContent(), parser$1) || [];
|
|
2250
2312
|
var markers = errors.map(function (_ref) {
|
|
2251
2313
|
var error = _ref.error,
|
|
2252
2314
|
boundary = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
@@ -3820,5 +3882,1925 @@ function SqlEditor(props) {
|
|
|
3820
3882
|
})));
|
|
3821
3883
|
}
|
|
3822
3884
|
|
|
3823
|
-
|
|
3885
|
+
var languageConfiguration$3 = {
|
|
3886
|
+
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
|
|
3887
|
+
comments: {
|
|
3888
|
+
lineComment: '//',
|
|
3889
|
+
blockComment: ['/*', '*/']
|
|
3890
|
+
},
|
|
3891
|
+
brackets: [['{', '}'], ['[', ']'], ['(', ')']],
|
|
3892
|
+
autoClosingPairs: [{
|
|
3893
|
+
open: '{',
|
|
3894
|
+
close: '}'
|
|
3895
|
+
}, {
|
|
3896
|
+
open: '[',
|
|
3897
|
+
close: ']'
|
|
3898
|
+
}, {
|
|
3899
|
+
open: '(',
|
|
3900
|
+
close: ')'
|
|
3901
|
+
}, {
|
|
3902
|
+
open: '"',
|
|
3903
|
+
close: '"'
|
|
3904
|
+
}, {
|
|
3905
|
+
open: "'",
|
|
3906
|
+
close: "'"
|
|
3907
|
+
}, {
|
|
3908
|
+
open: '`',
|
|
3909
|
+
close: '`'
|
|
3910
|
+
}],
|
|
3911
|
+
surroundingPairs: [{
|
|
3912
|
+
open: '{',
|
|
3913
|
+
close: '}'
|
|
3914
|
+
}, {
|
|
3915
|
+
open: '[',
|
|
3916
|
+
close: ']'
|
|
3917
|
+
}, {
|
|
3918
|
+
open: '(',
|
|
3919
|
+
close: ')'
|
|
3920
|
+
}, {
|
|
3921
|
+
open: '"',
|
|
3922
|
+
close: '"'
|
|
3923
|
+
}, {
|
|
3924
|
+
open: "'",
|
|
3925
|
+
close: "'"
|
|
3926
|
+
}, {
|
|
3927
|
+
open: '`',
|
|
3928
|
+
close: '`'
|
|
3929
|
+
}],
|
|
3930
|
+
folding: {
|
|
3931
|
+
offSide: false
|
|
3932
|
+
}
|
|
3933
|
+
};
|
|
3934
|
+
// Expr-lang keywords
|
|
3935
|
+
var keywords$3 = ['let', 'true', 'false', 'nil', 'in', 'not', 'and', 'or', 'if', 'else'];
|
|
3936
|
+
// Expr-lang built-in functions
|
|
3937
|
+
var stringFunctions = ['trim', 'trimPrefix', 'trimSuffix', 'upper', 'lower', 'split', 'splitAfter', 'replace', 'repeat', 'indexOf', 'lastIndexOf', 'hasPrefix', 'hasSuffix', 'contains', 'startsWith', 'endsWith'];
|
|
3938
|
+
var dateFunctions = ['now', 'duration', 'date', 'timezone'];
|
|
3939
|
+
var numberFunctions = ['max', 'min', 'abs', 'ceil', 'floor', 'round'];
|
|
3940
|
+
var arrayFunctions = ['all', 'any', 'one', 'none', 'map', 'filter', 'find', 'findIndex', 'findLast', 'findLastIndex', 'groupBy', 'count', 'concat', 'flatten', 'uniq', 'join', 'reduce', 'sum', 'mean', 'median', 'first', 'last', 'take', 'reverse', 'sort', 'sortBy'];
|
|
3941
|
+
var mapFunctions = ['keys', 'values'];
|
|
3942
|
+
var typeConversionFunctions = ['type', 'int', 'float', 'string', 'toJSON', 'fromJSON', 'toBase64', 'fromBase64', 'toPairs', 'fromPairs'];
|
|
3943
|
+
var miscFunctions = ['len', 'get'];
|
|
3944
|
+
var bitwiseFunctions = ['bitand', 'bitor', 'bitxor', 'bitnand', 'bitnot', 'bitshl', 'bitshr', 'bitushr'];
|
|
3945
|
+
var builtinFunctions = /*#__PURE__*/[].concat(stringFunctions, dateFunctions, numberFunctions, arrayFunctions, mapFunctions, typeConversionFunctions, miscFunctions, bitwiseFunctions);
|
|
3946
|
+
var language$3 = {
|
|
3947
|
+
defaultToken: '',
|
|
3948
|
+
tokenPostfix: '.expr',
|
|
3949
|
+
brackets: [{
|
|
3950
|
+
open: '(',
|
|
3951
|
+
close: ')',
|
|
3952
|
+
token: 'delimiter.parenthesis'
|
|
3953
|
+
}, {
|
|
3954
|
+
open: '{',
|
|
3955
|
+
close: '}',
|
|
3956
|
+
token: 'delimiter.curly'
|
|
3957
|
+
}, {
|
|
3958
|
+
open: '[',
|
|
3959
|
+
close: ']',
|
|
3960
|
+
token: 'delimiter.square'
|
|
3961
|
+
}],
|
|
3962
|
+
keywords: keywords$3,
|
|
3963
|
+
builtinFunctions: builtinFunctions,
|
|
3964
|
+
operators: ['+', '-', '*', '/', '%', '^', '**', '==', '!=', '<', '>', '<=', '>=', '!', '&&', '||', '?:', '??', '.', '?.', 'in', 'matches', '..', '|'],
|
|
3965
|
+
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
|
|
3966
|
+
digits: /\d+(_+\d+)*/,
|
|
3967
|
+
hexdigits: /[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,
|
|
3968
|
+
octdigits: /[0-7]+(_+[0-7]+)*/,
|
|
3969
|
+
bindigits: /[01]+(_+[01]+)*/,
|
|
3970
|
+
tokenizer: {
|
|
3971
|
+
root: [{
|
|
3972
|
+
include: '@whitespace'
|
|
3973
|
+
}, {
|
|
3974
|
+
include: '@comments'
|
|
3975
|
+
}, {
|
|
3976
|
+
include: '@numbers'
|
|
3977
|
+
}, {
|
|
3978
|
+
include: '@strings'
|
|
3979
|
+
}, {
|
|
3980
|
+
include: '@bytes'
|
|
3981
|
+
},
|
|
3982
|
+
// Function calls: identifier followed by '('
|
|
3983
|
+
[/[a-zA-Z_]\w*(?=\s*\()/, {
|
|
3984
|
+
cases: {
|
|
3985
|
+
'@builtinFunctions': 'keyword.function',
|
|
3986
|
+
'@default': 'identifier.function'
|
|
3987
|
+
}
|
|
3988
|
+
}],
|
|
3989
|
+
// Keywords and identifiers
|
|
3990
|
+
[/[a-zA-Z_]\w*/, {
|
|
3991
|
+
cases: {
|
|
3992
|
+
'@keywords': 'keyword',
|
|
3993
|
+
'@default': 'identifier'
|
|
3994
|
+
}
|
|
3995
|
+
}],
|
|
3996
|
+
// Operators
|
|
3997
|
+
[/[?][?:]/, 'operator'], [/[?][.]/, 'operator'], [/[.]{2}/, 'operator'], [/[*]{2}/, 'operator'], [/[|]/, 'operator'], [/[+\-*/%^]/, 'operator'], [/==|!=|<=|>=|<|>/, 'operator'], [/!|&&|\|\|/, 'operator'], [/[=]/, 'operator'], [/[.~]/, 'operator'],
|
|
3998
|
+
// Delimiters
|
|
3999
|
+
[/[,;]/, 'delimiter'], [/[{}()\[\]]/, '@brackets']],
|
|
4000
|
+
whitespace: [[/[ \t\r\n]+/, 'white']],
|
|
4001
|
+
comments: [[/\/\/.*$/, 'comment'], [/\/\*/, {
|
|
4002
|
+
token: 'comment.quote',
|
|
4003
|
+
next: '@comment'
|
|
4004
|
+
}]],
|
|
4005
|
+
comment: [[/[^*/]+/, 'comment'], [/\*\//, {
|
|
4006
|
+
token: 'comment.quote',
|
|
4007
|
+
next: '@pop'
|
|
4008
|
+
}], [/./, 'comment']],
|
|
4009
|
+
numbers: [[/0[xX]@hexdigits/, 'number.hex'], [/0[oO]@octdigits/, 'number.octal'], [/0[bB]@bindigits/, 'number.binary'], [/(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?/, 'number']],
|
|
4010
|
+
strings: [[/"/, {
|
|
4011
|
+
token: 'string.double',
|
|
4012
|
+
next: '@string_double'
|
|
4013
|
+
}], [/'/, {
|
|
4014
|
+
token: 'string',
|
|
4015
|
+
next: '@string_single'
|
|
4016
|
+
}], [/`/, {
|
|
4017
|
+
token: 'string.backtick',
|
|
4018
|
+
next: '@string_backtick'
|
|
4019
|
+
}]],
|
|
4020
|
+
string_double: [[/[^"\\]+/, 'string.double'], [/@escapes/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/"/, {
|
|
4021
|
+
token: 'string.double',
|
|
4022
|
+
next: '@pop'
|
|
4023
|
+
}]],
|
|
4024
|
+
string_single: [[/[^'\\]+/, 'string'], [/@escapes/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/'/, {
|
|
4025
|
+
token: 'string',
|
|
4026
|
+
next: '@pop'
|
|
4027
|
+
}]],
|
|
4028
|
+
string_backtick: [[/[^`]+/, 'string.backtick'], [/`/, {
|
|
4029
|
+
token: 'string.backtick',
|
|
4030
|
+
next: '@pop'
|
|
4031
|
+
}]],
|
|
4032
|
+
bytes: [[/[bB]"/, {
|
|
4033
|
+
token: 'string.bytes',
|
|
4034
|
+
next: '@bytes_double'
|
|
4035
|
+
}], [/[bB]'/, {
|
|
4036
|
+
token: 'string.bytes',
|
|
4037
|
+
next: '@bytes_single'
|
|
4038
|
+
}]],
|
|
4039
|
+
bytes_double: [[/[^"\\]+/, 'string.bytes'], [/\\(?:[abfnrtv\\"]|x[0-9A-Fa-f]{2}|[0-7]{3})/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/"/, {
|
|
4040
|
+
token: 'string.bytes',
|
|
4041
|
+
next: '@pop'
|
|
4042
|
+
}]],
|
|
4043
|
+
bytes_single: [[/[^'\\]+/, 'string.bytes'], [/\\(?:[abfnrtv\\']|x[0-9A-Fa-f]{2}|[0-7]{3})/, 'string.escape'], [/\\./, 'string.escape.invalid'], [/'/, {
|
|
4044
|
+
token: 'string.bytes',
|
|
4045
|
+
next: '@pop'
|
|
4046
|
+
}]]
|
|
4047
|
+
}
|
|
4048
|
+
};
|
|
4049
|
+
|
|
4050
|
+
var EXPR_KEYWORDS = ['let', 'true', 'false', 'nil', 'in', 'not', 'and', 'or', 'if', 'else'];
|
|
4051
|
+
var EXPR_FUNCTIONS = [
|
|
4052
|
+
// String functions
|
|
4053
|
+
{
|
|
4054
|
+
name: 'trim',
|
|
4055
|
+
signature: 'trim(str[, chars])',
|
|
4056
|
+
description: 'Removes white spaces from both ends of a string',
|
|
4057
|
+
category: 'string'
|
|
4058
|
+
}, {
|
|
4059
|
+
name: 'trimPrefix',
|
|
4060
|
+
signature: 'trimPrefix(str, prefix)',
|
|
4061
|
+
description: 'Removes the specified prefix from the string',
|
|
4062
|
+
category: 'string'
|
|
4063
|
+
}, {
|
|
4064
|
+
name: 'trimSuffix',
|
|
4065
|
+
signature: 'trimSuffix(str, suffix)',
|
|
4066
|
+
description: 'Removes the specified suffix from the string',
|
|
4067
|
+
category: 'string'
|
|
4068
|
+
}, {
|
|
4069
|
+
name: 'upper',
|
|
4070
|
+
signature: 'upper(str)',
|
|
4071
|
+
description: 'Converts all characters to uppercase',
|
|
4072
|
+
category: 'string'
|
|
4073
|
+
}, {
|
|
4074
|
+
name: 'lower',
|
|
4075
|
+
signature: 'lower(str)',
|
|
4076
|
+
description: 'Converts all characters to lowercase',
|
|
4077
|
+
category: 'string'
|
|
4078
|
+
}, {
|
|
4079
|
+
name: 'split',
|
|
4080
|
+
signature: 'split(str, delimiter[, n])',
|
|
4081
|
+
description: 'Splits a string at each instance of the delimiter',
|
|
4082
|
+
category: 'string'
|
|
4083
|
+
}, {
|
|
4084
|
+
name: 'splitAfter',
|
|
4085
|
+
signature: 'splitAfter(str, delimiter[, n])',
|
|
4086
|
+
description: 'Splits a string after each instance of the delimiter',
|
|
4087
|
+
category: 'string'
|
|
4088
|
+
}, {
|
|
4089
|
+
name: 'replace',
|
|
4090
|
+
signature: 'replace(str, old, new)',
|
|
4091
|
+
description: 'Replaces all occurrences of old with new',
|
|
4092
|
+
category: 'string'
|
|
4093
|
+
}, {
|
|
4094
|
+
name: 'repeat',
|
|
4095
|
+
signature: 'repeat(str, n)',
|
|
4096
|
+
description: 'Repeats a string n times',
|
|
4097
|
+
category: 'string'
|
|
4098
|
+
}, {
|
|
4099
|
+
name: 'indexOf',
|
|
4100
|
+
signature: 'indexOf(str, substring)',
|
|
4101
|
+
description: 'Returns the index of the first occurrence of substring',
|
|
4102
|
+
category: 'string'
|
|
4103
|
+
}, {
|
|
4104
|
+
name: 'lastIndexOf',
|
|
4105
|
+
signature: 'lastIndexOf(str, substring)',
|
|
4106
|
+
description: 'Returns the index of the last occurrence of substring',
|
|
4107
|
+
category: 'string'
|
|
4108
|
+
}, {
|
|
4109
|
+
name: 'hasPrefix',
|
|
4110
|
+
signature: 'hasPrefix(str, prefix)',
|
|
4111
|
+
description: 'Returns true if string starts with the given prefix',
|
|
4112
|
+
category: 'string'
|
|
4113
|
+
}, {
|
|
4114
|
+
name: 'hasSuffix',
|
|
4115
|
+
signature: 'hasSuffix(str, suffix)',
|
|
4116
|
+
description: 'Returns true if string ends with the given suffix',
|
|
4117
|
+
category: 'string'
|
|
4118
|
+
}, {
|
|
4119
|
+
name: 'contains',
|
|
4120
|
+
signature: 'contains(str, substr)',
|
|
4121
|
+
description: 'Returns true if string contains the given substring',
|
|
4122
|
+
category: 'string'
|
|
4123
|
+
}, {
|
|
4124
|
+
name: 'startsWith',
|
|
4125
|
+
signature: 'startsWith(str, prefix)',
|
|
4126
|
+
description: 'Returns true if string starts with the given prefix',
|
|
4127
|
+
category: 'string'
|
|
4128
|
+
}, {
|
|
4129
|
+
name: 'endsWith',
|
|
4130
|
+
signature: 'endsWith(str, suffix)',
|
|
4131
|
+
description: 'Returns true if string ends with the given suffix',
|
|
4132
|
+
category: 'string'
|
|
4133
|
+
},
|
|
4134
|
+
// Date functions
|
|
4135
|
+
{
|
|
4136
|
+
name: 'now',
|
|
4137
|
+
signature: 'now()',
|
|
4138
|
+
description: 'Returns the current date as a time.Time value',
|
|
4139
|
+
category: 'date'
|
|
4140
|
+
}, {
|
|
4141
|
+
name: 'duration',
|
|
4142
|
+
signature: 'duration(str)',
|
|
4143
|
+
description: 'Returns time.Duration value of the given string',
|
|
4144
|
+
category: 'date'
|
|
4145
|
+
}, {
|
|
4146
|
+
name: 'date',
|
|
4147
|
+
signature: 'date(str[, format[, timezone]])',
|
|
4148
|
+
description: 'Converts the given string into a date representation',
|
|
4149
|
+
category: 'date'
|
|
4150
|
+
}, {
|
|
4151
|
+
name: 'timezone',
|
|
4152
|
+
signature: 'timezone(str)',
|
|
4153
|
+
description: 'Returns the timezone of the given string',
|
|
4154
|
+
category: 'date'
|
|
4155
|
+
},
|
|
4156
|
+
// Number functions
|
|
4157
|
+
{
|
|
4158
|
+
name: 'max',
|
|
4159
|
+
signature: 'max(n1, n2)',
|
|
4160
|
+
description: 'Returns the maximum of two numbers',
|
|
4161
|
+
category: 'number'
|
|
4162
|
+
}, {
|
|
4163
|
+
name: 'min',
|
|
4164
|
+
signature: 'min(n1, n2)',
|
|
4165
|
+
description: 'Returns the minimum of two numbers',
|
|
4166
|
+
category: 'number'
|
|
4167
|
+
}, {
|
|
4168
|
+
name: 'abs',
|
|
4169
|
+
signature: 'abs(n)',
|
|
4170
|
+
description: 'Returns the absolute value of a number',
|
|
4171
|
+
category: 'number'
|
|
4172
|
+
}, {
|
|
4173
|
+
name: 'ceil',
|
|
4174
|
+
signature: 'ceil(n)',
|
|
4175
|
+
description: 'Returns the least integer value greater than or equal to x',
|
|
4176
|
+
category: 'number'
|
|
4177
|
+
}, {
|
|
4178
|
+
name: 'floor',
|
|
4179
|
+
signature: 'floor(n)',
|
|
4180
|
+
description: 'Returns the greatest integer value less than or equal to x',
|
|
4181
|
+
category: 'number'
|
|
4182
|
+
}, {
|
|
4183
|
+
name: 'round',
|
|
4184
|
+
signature: 'round(n)',
|
|
4185
|
+
description: 'Returns the nearest integer, rounding half away from zero',
|
|
4186
|
+
category: 'number'
|
|
4187
|
+
},
|
|
4188
|
+
// Array functions
|
|
4189
|
+
{
|
|
4190
|
+
name: 'all',
|
|
4191
|
+
signature: 'all(array, predicate)',
|
|
4192
|
+
description: 'Returns true if all elements satisfy the predicate',
|
|
4193
|
+
category: 'array'
|
|
4194
|
+
}, {
|
|
4195
|
+
name: 'any',
|
|
4196
|
+
signature: 'any(array, predicate)',
|
|
4197
|
+
description: 'Returns true if any elements satisfy the predicate',
|
|
4198
|
+
category: 'array'
|
|
4199
|
+
}, {
|
|
4200
|
+
name: 'one',
|
|
4201
|
+
signature: 'one(array, predicate)',
|
|
4202
|
+
description: 'Returns true if exactly one element satisfies the predicate',
|
|
4203
|
+
category: 'array'
|
|
4204
|
+
}, {
|
|
4205
|
+
name: 'none',
|
|
4206
|
+
signature: 'none(array, predicate)',
|
|
4207
|
+
description: 'Returns true if no elements satisfy the predicate',
|
|
4208
|
+
category: 'array'
|
|
4209
|
+
}, {
|
|
4210
|
+
name: 'map',
|
|
4211
|
+
signature: 'map(array, predicate)',
|
|
4212
|
+
description: 'Returns new array by applying the predicate to each element',
|
|
4213
|
+
category: 'array'
|
|
4214
|
+
}, {
|
|
4215
|
+
name: 'filter',
|
|
4216
|
+
signature: 'filter(array, predicate)',
|
|
4217
|
+
description: 'Returns new array by filtering elements by predicate',
|
|
4218
|
+
category: 'array'
|
|
4219
|
+
}, {
|
|
4220
|
+
name: 'find',
|
|
4221
|
+
signature: 'find(array, predicate)',
|
|
4222
|
+
description: 'Finds the first element satisfying the predicate',
|
|
4223
|
+
category: 'array'
|
|
4224
|
+
}, {
|
|
4225
|
+
name: 'findIndex',
|
|
4226
|
+
signature: 'findIndex(array, predicate)',
|
|
4227
|
+
description: 'Finds the index of the first element satisfying the predicate',
|
|
4228
|
+
category: 'array'
|
|
4229
|
+
}, {
|
|
4230
|
+
name: 'findLast',
|
|
4231
|
+
signature: 'findLast(array, predicate)',
|
|
4232
|
+
description: 'Finds the last element satisfying the predicate',
|
|
4233
|
+
category: 'array'
|
|
4234
|
+
}, {
|
|
4235
|
+
name: 'findLastIndex',
|
|
4236
|
+
signature: 'findLastIndex(array, predicate)',
|
|
4237
|
+
description: 'Finds the index of the last element satisfying the predicate',
|
|
4238
|
+
category: 'array'
|
|
4239
|
+
}, {
|
|
4240
|
+
name: 'groupBy',
|
|
4241
|
+
signature: 'groupBy(array, predicate)',
|
|
4242
|
+
description: 'Groups elements by the result of the predicate',
|
|
4243
|
+
category: 'array'
|
|
4244
|
+
}, {
|
|
4245
|
+
name: 'count',
|
|
4246
|
+
signature: 'count(array[, predicate])',
|
|
4247
|
+
description: 'Returns the number of elements satisfying the predicate',
|
|
4248
|
+
category: 'array'
|
|
4249
|
+
}, {
|
|
4250
|
+
name: 'concat',
|
|
4251
|
+
signature: 'concat(array1, array2[, ...])',
|
|
4252
|
+
description: 'Concatenates two or more arrays',
|
|
4253
|
+
category: 'array'
|
|
4254
|
+
}, {
|
|
4255
|
+
name: 'flatten',
|
|
4256
|
+
signature: 'flatten(array)',
|
|
4257
|
+
description: 'Flattens an array into one-dimensional array',
|
|
4258
|
+
category: 'array'
|
|
4259
|
+
}, {
|
|
4260
|
+
name: 'uniq',
|
|
4261
|
+
signature: 'uniq(array)',
|
|
4262
|
+
description: 'Removes duplicates from an array',
|
|
4263
|
+
category: 'array'
|
|
4264
|
+
}, {
|
|
4265
|
+
name: 'join',
|
|
4266
|
+
signature: 'join(array[, delimiter])',
|
|
4267
|
+
description: 'Joins an array of strings into a single string',
|
|
4268
|
+
category: 'array'
|
|
4269
|
+
}, {
|
|
4270
|
+
name: 'reduce',
|
|
4271
|
+
signature: 'reduce(array, predicate[, initial])',
|
|
4272
|
+
description: 'Reduces an array to a single value',
|
|
4273
|
+
category: 'array'
|
|
4274
|
+
}, {
|
|
4275
|
+
name: 'sum',
|
|
4276
|
+
signature: 'sum(array[, predicate])',
|
|
4277
|
+
description: 'Returns the sum of all numbers in an array',
|
|
4278
|
+
category: 'array'
|
|
4279
|
+
}, {
|
|
4280
|
+
name: 'mean',
|
|
4281
|
+
signature: 'mean(array)',
|
|
4282
|
+
description: 'Returns the average of all numbers in an array',
|
|
4283
|
+
category: 'array'
|
|
4284
|
+
}, {
|
|
4285
|
+
name: 'median',
|
|
4286
|
+
signature: 'median(array)',
|
|
4287
|
+
description: 'Returns the median of all numbers in an array',
|
|
4288
|
+
category: 'array'
|
|
4289
|
+
}, {
|
|
4290
|
+
name: 'first',
|
|
4291
|
+
signature: 'first(array)',
|
|
4292
|
+
description: 'Returns the first element from an array',
|
|
4293
|
+
category: 'array'
|
|
4294
|
+
}, {
|
|
4295
|
+
name: 'last',
|
|
4296
|
+
signature: 'last(array)',
|
|
4297
|
+
description: 'Returns the last element from an array',
|
|
4298
|
+
category: 'array'
|
|
4299
|
+
}, {
|
|
4300
|
+
name: 'take',
|
|
4301
|
+
signature: 'take(array, n)',
|
|
4302
|
+
description: 'Returns the first n elements from an array',
|
|
4303
|
+
category: 'array'
|
|
4304
|
+
}, {
|
|
4305
|
+
name: 'reverse',
|
|
4306
|
+
signature: 'reverse(array)',
|
|
4307
|
+
description: 'Returns a new reversed copy of the array',
|
|
4308
|
+
category: 'array'
|
|
4309
|
+
}, {
|
|
4310
|
+
name: 'sort',
|
|
4311
|
+
signature: 'sort(array[, order])',
|
|
4312
|
+
description: 'Sorts an array in ascending or descending order',
|
|
4313
|
+
category: 'array'
|
|
4314
|
+
}, {
|
|
4315
|
+
name: 'sortBy',
|
|
4316
|
+
signature: 'sortBy(array[, predicate, order])',
|
|
4317
|
+
description: 'Sorts an array by the result of the predicate',
|
|
4318
|
+
category: 'array'
|
|
4319
|
+
},
|
|
4320
|
+
// Map functions
|
|
4321
|
+
{
|
|
4322
|
+
name: 'keys',
|
|
4323
|
+
signature: 'keys(map)',
|
|
4324
|
+
description: 'Returns an array containing the keys of the map',
|
|
4325
|
+
category: 'map'
|
|
4326
|
+
}, {
|
|
4327
|
+
name: 'values',
|
|
4328
|
+
signature: 'values(map)',
|
|
4329
|
+
description: 'Returns an array containing the values of the map',
|
|
4330
|
+
category: 'map'
|
|
4331
|
+
},
|
|
4332
|
+
// Type conversion functions
|
|
4333
|
+
{
|
|
4334
|
+
name: 'type',
|
|
4335
|
+
signature: 'type(v)',
|
|
4336
|
+
description: 'Returns the type of the given value',
|
|
4337
|
+
category: 'conversion'
|
|
4338
|
+
}, {
|
|
4339
|
+
name: 'int',
|
|
4340
|
+
signature: 'int(v)',
|
|
4341
|
+
description: 'Returns the integer value of a number or string',
|
|
4342
|
+
category: 'conversion'
|
|
4343
|
+
}, {
|
|
4344
|
+
name: 'float',
|
|
4345
|
+
signature: 'float(v)',
|
|
4346
|
+
description: 'Returns the float value of a number or string',
|
|
4347
|
+
category: 'conversion'
|
|
4348
|
+
}, {
|
|
4349
|
+
name: 'string',
|
|
4350
|
+
signature: 'string(v)',
|
|
4351
|
+
description: 'Converts a value to its string representation',
|
|
4352
|
+
category: 'conversion'
|
|
4353
|
+
}, {
|
|
4354
|
+
name: 'toJSON',
|
|
4355
|
+
signature: 'toJSON(v)',
|
|
4356
|
+
description: 'Converts a value to JSON string representation',
|
|
4357
|
+
category: 'conversion'
|
|
4358
|
+
}, {
|
|
4359
|
+
name: 'fromJSON',
|
|
4360
|
+
signature: 'fromJSON(v)',
|
|
4361
|
+
description: 'Parses a JSON string to a value',
|
|
4362
|
+
category: 'conversion'
|
|
4363
|
+
}, {
|
|
4364
|
+
name: 'toBase64',
|
|
4365
|
+
signature: 'toBase64(v)',
|
|
4366
|
+
description: 'Encodes a string into Base64 format',
|
|
4367
|
+
category: 'conversion'
|
|
4368
|
+
}, {
|
|
4369
|
+
name: 'fromBase64',
|
|
4370
|
+
signature: 'fromBase64(v)',
|
|
4371
|
+
description: 'Decodes a Base64 encoded string',
|
|
4372
|
+
category: 'conversion'
|
|
4373
|
+
}, {
|
|
4374
|
+
name: 'toPairs',
|
|
4375
|
+
signature: 'toPairs(map)',
|
|
4376
|
+
description: 'Converts a map to an array of key-value pairs',
|
|
4377
|
+
category: 'conversion'
|
|
4378
|
+
}, {
|
|
4379
|
+
name: 'fromPairs',
|
|
4380
|
+
signature: 'fromPairs(array)',
|
|
4381
|
+
description: 'Converts an array of key-value pairs to a map',
|
|
4382
|
+
category: 'conversion'
|
|
4383
|
+
},
|
|
4384
|
+
// Miscellaneous functions
|
|
4385
|
+
{
|
|
4386
|
+
name: 'len',
|
|
4387
|
+
signature: 'len(v)',
|
|
4388
|
+
description: 'Returns the length of an array, map, or string',
|
|
4389
|
+
category: 'misc'
|
|
4390
|
+
}, {
|
|
4391
|
+
name: 'get',
|
|
4392
|
+
signature: 'get(v, index)',
|
|
4393
|
+
description: 'Retrieves element at the specified index from an array or map',
|
|
4394
|
+
category: 'misc'
|
|
4395
|
+
},
|
|
4396
|
+
// Bitwise functions
|
|
4397
|
+
{
|
|
4398
|
+
name: 'bitand',
|
|
4399
|
+
signature: 'bitand(int, int)',
|
|
4400
|
+
description: 'Returns the bitwise AND of two integers',
|
|
4401
|
+
category: 'bitwise'
|
|
4402
|
+
}, {
|
|
4403
|
+
name: 'bitor',
|
|
4404
|
+
signature: 'bitor(int, int)',
|
|
4405
|
+
description: 'Returns the bitwise OR of two integers',
|
|
4406
|
+
category: 'bitwise'
|
|
4407
|
+
}, {
|
|
4408
|
+
name: 'bitxor',
|
|
4409
|
+
signature: 'bitxor(int, int)',
|
|
4410
|
+
description: 'Returns the bitwise XOR of two integers',
|
|
4411
|
+
category: 'bitwise'
|
|
4412
|
+
}, {
|
|
4413
|
+
name: 'bitnand',
|
|
4414
|
+
signature: 'bitnand(int, int)',
|
|
4415
|
+
description: 'Returns the bitwise AND NOT of two integers',
|
|
4416
|
+
category: 'bitwise'
|
|
4417
|
+
}, {
|
|
4418
|
+
name: 'bitnot',
|
|
4419
|
+
signature: 'bitnot(int)',
|
|
4420
|
+
description: 'Returns the bitwise NOT of an integer',
|
|
4421
|
+
category: 'bitwise'
|
|
4422
|
+
}, {
|
|
4423
|
+
name: 'bitshl',
|
|
4424
|
+
signature: 'bitshl(int, int)',
|
|
4425
|
+
description: 'Returns the Left Shift of an integer',
|
|
4426
|
+
category: 'bitwise'
|
|
4427
|
+
}, {
|
|
4428
|
+
name: 'bitshr',
|
|
4429
|
+
signature: 'bitshr(int, int)',
|
|
4430
|
+
description: 'Returns the Right Shift of an integer',
|
|
4431
|
+
category: 'bitwise'
|
|
4432
|
+
}, {
|
|
4433
|
+
name: 'bitushr',
|
|
4434
|
+
signature: 'bitushr(int, int)',
|
|
4435
|
+
description: 'Returns the unsigned Right Shift of an integer',
|
|
4436
|
+
category: 'bitwise'
|
|
4437
|
+
}];
|
|
4438
|
+
var getExprCompletionProvider = function getExprCompletionProvider() {
|
|
4439
|
+
return {
|
|
4440
|
+
provideCompletionItems: function provideCompletionItems(model, position, _context, _token) {
|
|
4441
|
+
var word = model.getWordUntilPosition(position);
|
|
4442
|
+
var range = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
|
|
4443
|
+
var suggestions = [].concat(EXPR_KEYWORDS.map(function (keyword) {
|
|
4444
|
+
return {
|
|
4445
|
+
label: keyword,
|
|
4446
|
+
kind: languages.CompletionItemKind.Keyword,
|
|
4447
|
+
insertText: keyword,
|
|
4448
|
+
range: range,
|
|
4449
|
+
sortText: '1' + keyword
|
|
4450
|
+
};
|
|
4451
|
+
}), EXPR_FUNCTIONS.map(function (func) {
|
|
4452
|
+
return {
|
|
4453
|
+
label: func.name,
|
|
4454
|
+
kind: languages.CompletionItemKind.Function,
|
|
4455
|
+
insertText: func.name,
|
|
4456
|
+
detail: func.signature,
|
|
4457
|
+
documentation: func.description + (" (" + func.category + ")"),
|
|
4458
|
+
range: range,
|
|
4459
|
+
sortText: '2' + func.name,
|
|
4460
|
+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet
|
|
4461
|
+
};
|
|
4462
|
+
}));
|
|
4463
|
+
return {
|
|
4464
|
+
suggestions: suggestions
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
};
|
|
4468
|
+
};
|
|
4469
|
+
|
|
4470
|
+
/**
|
|
4471
|
+
* Expr-lang token types and AST definitions
|
|
4472
|
+
*/
|
|
4473
|
+
var TokenKind;
|
|
4474
|
+
(function (TokenKind) {
|
|
4475
|
+
TokenKind["Identifier"] = "Identifier";
|
|
4476
|
+
TokenKind["Number"] = "Number";
|
|
4477
|
+
TokenKind["String"] = "String";
|
|
4478
|
+
TokenKind["Operator"] = "Operator";
|
|
4479
|
+
TokenKind["Bracket"] = "Bracket";
|
|
4480
|
+
TokenKind["EOF"] = "EOF";
|
|
4481
|
+
})(TokenKind || (TokenKind = {}));
|
|
4482
|
+
var EOF_TOKEN = {
|
|
4483
|
+
kind: TokenKind.EOF,
|
|
4484
|
+
value: '',
|
|
4485
|
+
start: 0,
|
|
4486
|
+
end: 0,
|
|
4487
|
+
line: 0,
|
|
4488
|
+
column: 0
|
|
4489
|
+
};
|
|
4490
|
+
// Operator precedence (higher = binds tighter)
|
|
4491
|
+
var OPERATOR_PRECEDENCE = {
|
|
4492
|
+
'??': 1,
|
|
4493
|
+
'||': 2,
|
|
4494
|
+
or: 2,
|
|
4495
|
+
'&&': 3,
|
|
4496
|
+
and: 3,
|
|
4497
|
+
'==': 4,
|
|
4498
|
+
'!=': 4,
|
|
4499
|
+
'<': 5,
|
|
4500
|
+
'>': 5,
|
|
4501
|
+
'<=': 5,
|
|
4502
|
+
'>=': 5,
|
|
4503
|
+
"in": 5,
|
|
4504
|
+
matches: 5,
|
|
4505
|
+
contains: 5,
|
|
4506
|
+
startsWith: 5,
|
|
4507
|
+
endsWith: 5,
|
|
4508
|
+
'+': 6,
|
|
4509
|
+
'-': 6,
|
|
4510
|
+
'*': 7,
|
|
4511
|
+
'/': 7,
|
|
4512
|
+
'%': 7,
|
|
4513
|
+
'^': 8,
|
|
4514
|
+
'**': 8
|
|
4515
|
+
};
|
|
4516
|
+
// Unary operators
|
|
4517
|
+
var UNARY_OPERATORS = /*#__PURE__*/new Set(['!', 'not', '-']);
|
|
4518
|
+
// Comparison operators (for chained comparisons)
|
|
4519
|
+
var COMPARISON_OPERATORS = /*#__PURE__*/new Set(['<', '>', '<=', '>=', '==', '!=']);
|
|
4520
|
+
|
|
4521
|
+
// Tokens that should be treated as Operators even if they look like identifiers
|
|
4522
|
+
var OPERATOR_TOKENS = /*#__PURE__*/new Set(['let', 'if', 'else', 'not', 'and', 'or', 'in', 'matches', 'contains', 'startsWith', 'endsWith', 'hasPrefix', 'hasSuffix']);
|
|
4523
|
+
var Lexer = /*#__PURE__*/function () {
|
|
4524
|
+
function Lexer() {
|
|
4525
|
+
this.input = '';
|
|
4526
|
+
this.pos = 0;
|
|
4527
|
+
this.line = 1;
|
|
4528
|
+
this.column = 1;
|
|
4529
|
+
this.startLine = 1;
|
|
4530
|
+
this.startColumn = 1;
|
|
4531
|
+
}
|
|
4532
|
+
var _proto = Lexer.prototype;
|
|
4533
|
+
_proto.reset = function reset(input) {
|
|
4534
|
+
this.input = input;
|
|
4535
|
+
this.pos = 0;
|
|
4536
|
+
this.line = 1;
|
|
4537
|
+
this.column = 1;
|
|
4538
|
+
this.startLine = 1;
|
|
4539
|
+
this.startColumn = 1;
|
|
4540
|
+
};
|
|
4541
|
+
_proto.next = function next() {
|
|
4542
|
+
this.skipWhitespace();
|
|
4543
|
+
this.skipComments();
|
|
4544
|
+
if (this.pos >= this.input.length) {
|
|
4545
|
+
return _extends({}, EOF_TOKEN, {
|
|
4546
|
+
start: this.pos,
|
|
4547
|
+
end: this.pos,
|
|
4548
|
+
line: this.line,
|
|
4549
|
+
column: this.column
|
|
4550
|
+
});
|
|
4551
|
+
}
|
|
4552
|
+
this.startLine = this.line;
|
|
4553
|
+
this.startColumn = this.column;
|
|
4554
|
+
var ch = this.input[this.pos];
|
|
4555
|
+
// String literals
|
|
4556
|
+
if (ch === '"' || ch === "'") {
|
|
4557
|
+
return this.readString(ch);
|
|
4558
|
+
}
|
|
4559
|
+
if (ch === '`') {
|
|
4560
|
+
return this.readBacktickString();
|
|
4561
|
+
}
|
|
4562
|
+
// Byte literals: b"..." or b'...' or B"..." or B'...'
|
|
4563
|
+
if ((ch === 'b' || ch === 'B') && this.pos + 1 < this.input.length) {
|
|
4564
|
+
var _next = this.input[this.pos + 1];
|
|
4565
|
+
if (_next === '"' || _next === "'") {
|
|
4566
|
+
return this.readByteLiteral();
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
// Numbers
|
|
4570
|
+
if (this.isDigit(ch) || ch === '.' && this.pos + 1 < this.input.length && this.isDigit(this.input[this.pos + 1])) {
|
|
4571
|
+
return this.readNumber();
|
|
4572
|
+
}
|
|
4573
|
+
// Identifiers and keywords
|
|
4574
|
+
if (this.isIdentStart(ch)) {
|
|
4575
|
+
var token = this.readIdentOrKeyword();
|
|
4576
|
+
return token;
|
|
4577
|
+
}
|
|
4578
|
+
// Operators and brackets
|
|
4579
|
+
return this.readOperatorOrBracket();
|
|
4580
|
+
};
|
|
4581
|
+
_proto.skipWhitespace = function skipWhitespace() {
|
|
4582
|
+
while (this.pos < this.input.length) {
|
|
4583
|
+
var ch = this.input[this.pos];
|
|
4584
|
+
if (ch === ' ' || ch === '\t' || ch === '\r') {
|
|
4585
|
+
this.pos++;
|
|
4586
|
+
this.column++;
|
|
4587
|
+
} else if (ch === '\n') {
|
|
4588
|
+
this.pos++;
|
|
4589
|
+
this.line++;
|
|
4590
|
+
this.column = 1;
|
|
4591
|
+
} else {
|
|
4592
|
+
break;
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
};
|
|
4596
|
+
_proto.skipComments = function skipComments() {
|
|
4597
|
+
while (this.pos < this.input.length) {
|
|
4598
|
+
// Line comment //
|
|
4599
|
+
if (this.input[this.pos] === '/' && this.pos + 1 < this.input.length && this.input[this.pos + 1] === '/') {
|
|
4600
|
+
this.pos += 2;
|
|
4601
|
+
this.column += 2;
|
|
4602
|
+
while (this.pos < this.input.length && this.input[this.pos] !== '\n') {
|
|
4603
|
+
this.pos++;
|
|
4604
|
+
this.column++;
|
|
4605
|
+
}
|
|
4606
|
+
this.skipWhitespace();
|
|
4607
|
+
continue;
|
|
4608
|
+
}
|
|
4609
|
+
// Block comment /* */
|
|
4610
|
+
if (this.input[this.pos] === '/' && this.pos + 1 < this.input.length && this.input[this.pos + 1] === '*') {
|
|
4611
|
+
this.pos += 2;
|
|
4612
|
+
this.column += 2;
|
|
4613
|
+
while (this.pos < this.input.length) {
|
|
4614
|
+
if (this.input[this.pos] === '*' && this.pos + 1 < this.input.length && this.input[this.pos + 1] === '/') {
|
|
4615
|
+
this.pos += 2;
|
|
4616
|
+
this.column += 2;
|
|
4617
|
+
break;
|
|
4618
|
+
}
|
|
4619
|
+
if (this.input[this.pos] === '\n') {
|
|
4620
|
+
this.line++;
|
|
4621
|
+
this.column = 1;
|
|
4622
|
+
} else {
|
|
4623
|
+
this.column++;
|
|
4624
|
+
}
|
|
4625
|
+
this.pos++;
|
|
4626
|
+
}
|
|
4627
|
+
this.skipWhitespace();
|
|
4628
|
+
continue;
|
|
4629
|
+
}
|
|
4630
|
+
break;
|
|
4631
|
+
}
|
|
4632
|
+
};
|
|
4633
|
+
_proto.readString = function readString(quote) {
|
|
4634
|
+
var start = this.pos;
|
|
4635
|
+
this.pos++; // skip opening quote
|
|
4636
|
+
this.column++;
|
|
4637
|
+
var value = '';
|
|
4638
|
+
while (this.pos < this.input.length) {
|
|
4639
|
+
var ch = this.input[this.pos];
|
|
4640
|
+
if (ch === '\\') {
|
|
4641
|
+
if (this.pos + 1 >= this.input.length) break;
|
|
4642
|
+
var next = this.input[this.pos + 1];
|
|
4643
|
+
switch (next) {
|
|
4644
|
+
case 'n':
|
|
4645
|
+
value += '\n';
|
|
4646
|
+
break;
|
|
4647
|
+
case 't':
|
|
4648
|
+
value += '\t';
|
|
4649
|
+
break;
|
|
4650
|
+
case 'r':
|
|
4651
|
+
value += '\r';
|
|
4652
|
+
break;
|
|
4653
|
+
case '\\':
|
|
4654
|
+
value += '\\';
|
|
4655
|
+
break;
|
|
4656
|
+
case quote:
|
|
4657
|
+
value += quote;
|
|
4658
|
+
break;
|
|
4659
|
+
case 'x':
|
|
4660
|
+
{
|
|
4661
|
+
var hex = this.input.substr(this.pos + 2, 2);
|
|
4662
|
+
value += String.fromCharCode(parseInt(hex, 16));
|
|
4663
|
+
this.pos += 2;
|
|
4664
|
+
this.column += 2;
|
|
4665
|
+
break;
|
|
4666
|
+
}
|
|
4667
|
+
case 'u':
|
|
4668
|
+
{
|
|
4669
|
+
var unicode = this.input.substr(this.pos + 2, 4);
|
|
4670
|
+
value += String.fromCharCode(parseInt(unicode, 16));
|
|
4671
|
+
this.pos += 2;
|
|
4672
|
+
this.column += 2;
|
|
4673
|
+
break;
|
|
4674
|
+
}
|
|
4675
|
+
default:
|
|
4676
|
+
value += next;
|
|
4677
|
+
}
|
|
4678
|
+
this.pos += 2;
|
|
4679
|
+
this.column += 2;
|
|
4680
|
+
} else if (ch === quote) {
|
|
4681
|
+
this.pos++; // skip closing quote
|
|
4682
|
+
this.column++;
|
|
4683
|
+
return this.makeToken(TokenKind.String, value, start);
|
|
4684
|
+
} else {
|
|
4685
|
+
value += ch;
|
|
4686
|
+
this.pos++;
|
|
4687
|
+
this.column++;
|
|
4688
|
+
if (ch === '\n') {
|
|
4689
|
+
this.line++;
|
|
4690
|
+
this.column = 1;
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
return this.makeToken(TokenKind.String, value, start);
|
|
4695
|
+
};
|
|
4696
|
+
_proto.readBacktickString = function readBacktickString() {
|
|
4697
|
+
var start = this.pos;
|
|
4698
|
+
this.pos++; // skip opening backtick
|
|
4699
|
+
this.column++;
|
|
4700
|
+
var value = '';
|
|
4701
|
+
while (this.pos < this.input.length) {
|
|
4702
|
+
var ch = this.input[this.pos];
|
|
4703
|
+
if (ch === '`') {
|
|
4704
|
+
this.pos++;
|
|
4705
|
+
this.column++;
|
|
4706
|
+
return this.makeToken(TokenKind.String, value, start);
|
|
4707
|
+
}
|
|
4708
|
+
value += ch;
|
|
4709
|
+
this.pos++;
|
|
4710
|
+
if (ch === '\n') {
|
|
4711
|
+
this.line++;
|
|
4712
|
+
this.column = 1;
|
|
4713
|
+
} else {
|
|
4714
|
+
this.column++;
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
return this.makeToken(TokenKind.String, value, start);
|
|
4718
|
+
};
|
|
4719
|
+
_proto.readByteLiteral = function readByteLiteral() {
|
|
4720
|
+
// b"..." or b'...' or B"..." or B'...'
|
|
4721
|
+
var start = this.pos;
|
|
4722
|
+
this.pos++; // skip 'b' or 'B'
|
|
4723
|
+
this.column++;
|
|
4724
|
+
var quote = this.input[this.pos];
|
|
4725
|
+
this.pos++; // skip quote
|
|
4726
|
+
this.column++;
|
|
4727
|
+
var value = '';
|
|
4728
|
+
while (this.pos < this.input.length) {
|
|
4729
|
+
var ch = this.input[this.pos];
|
|
4730
|
+
if (ch === '\\') {
|
|
4731
|
+
if (this.pos + 1 >= this.input.length) break;
|
|
4732
|
+
var next = this.input[this.pos + 1];
|
|
4733
|
+
switch (next) {
|
|
4734
|
+
case 'n':
|
|
4735
|
+
value += '\n';
|
|
4736
|
+
break;
|
|
4737
|
+
case 't':
|
|
4738
|
+
value += '\t';
|
|
4739
|
+
break;
|
|
4740
|
+
case 'r':
|
|
4741
|
+
value += '\r';
|
|
4742
|
+
break;
|
|
4743
|
+
case '\\':
|
|
4744
|
+
value += '\\';
|
|
4745
|
+
break;
|
|
4746
|
+
case quote:
|
|
4747
|
+
value += quote;
|
|
4748
|
+
break;
|
|
4749
|
+
case 'x':
|
|
4750
|
+
{
|
|
4751
|
+
var hex = this.input.substr(this.pos + 2, 2);
|
|
4752
|
+
value += String.fromCharCode(parseInt(hex, 16));
|
|
4753
|
+
this.pos += 2;
|
|
4754
|
+
this.column += 2;
|
|
4755
|
+
break;
|
|
4756
|
+
}
|
|
4757
|
+
default:
|
|
4758
|
+
// octal escape \NNN
|
|
4759
|
+
if (this.isDigit(next)) {
|
|
4760
|
+
var octal = this.input.substr(this.pos + 1, 3);
|
|
4761
|
+
value += String.fromCharCode(parseInt(octal, 8));
|
|
4762
|
+
this.pos += 2;
|
|
4763
|
+
this.column += 2;
|
|
4764
|
+
break;
|
|
4765
|
+
}
|
|
4766
|
+
value += next;
|
|
4767
|
+
}
|
|
4768
|
+
this.pos += 2;
|
|
4769
|
+
this.column += 2;
|
|
4770
|
+
} else if (ch === quote) {
|
|
4771
|
+
this.pos++;
|
|
4772
|
+
this.column++;
|
|
4773
|
+
return this.makeToken(TokenKind.String, value, start);
|
|
4774
|
+
} else {
|
|
4775
|
+
value += ch;
|
|
4776
|
+
this.pos++;
|
|
4777
|
+
this.column++;
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
return this.makeToken(TokenKind.String, value, start);
|
|
4781
|
+
};
|
|
4782
|
+
_proto.readNumber = function readNumber() {
|
|
4783
|
+
var start = this.pos;
|
|
4784
|
+
var value = '';
|
|
4785
|
+
while (this.pos < this.input.length) {
|
|
4786
|
+
var ch = this.input[this.pos];
|
|
4787
|
+
if (this.isDigit(ch) || ch === '.' || ch === 'e' || ch === 'E' || ch === '+' || ch === '-' || ch === '_' || ch === 'x' || ch === 'X' || ch === 'o' || ch === 'O' || ch === 'b' || ch === 'B' || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F') {
|
|
4788
|
+
value += ch;
|
|
4789
|
+
this.pos++;
|
|
4790
|
+
this.column++;
|
|
4791
|
+
} else {
|
|
4792
|
+
break;
|
|
4793
|
+
}
|
|
4794
|
+
}
|
|
4795
|
+
return this.makeToken(TokenKind.Number, value, start);
|
|
4796
|
+
};
|
|
4797
|
+
_proto.readIdentOrKeyword = function readIdentOrKeyword() {
|
|
4798
|
+
var start = this.pos;
|
|
4799
|
+
var value = '';
|
|
4800
|
+
while (this.pos < this.input.length) {
|
|
4801
|
+
var ch = this.input[this.pos];
|
|
4802
|
+
if (this.isIdentPart(ch)) {
|
|
4803
|
+
value += ch;
|
|
4804
|
+
this.pos++;
|
|
4805
|
+
this.column++;
|
|
4806
|
+
} else {
|
|
4807
|
+
break;
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
// Keywords that are operators
|
|
4811
|
+
if (OPERATOR_TOKENS.has(value)) {
|
|
4812
|
+
return this.makeToken(TokenKind.Operator, value, start);
|
|
4813
|
+
}
|
|
4814
|
+
return this.makeToken(TokenKind.Identifier, value, start);
|
|
4815
|
+
};
|
|
4816
|
+
_proto.readOperatorOrBracket = function readOperatorOrBracket() {
|
|
4817
|
+
var start = this.pos;
|
|
4818
|
+
var ch = this.input[this.pos];
|
|
4819
|
+
// Brackets
|
|
4820
|
+
if ('()[]{}'.includes(ch)) {
|
|
4821
|
+
this.pos++;
|
|
4822
|
+
this.column++;
|
|
4823
|
+
return this.makeToken(TokenKind.Bracket, ch, start);
|
|
4824
|
+
}
|
|
4825
|
+
// Multi-character operators
|
|
4826
|
+
var twoChar = this.input.substr(this.pos, 2);
|
|
4827
|
+
var threeChar = this.input.substr(this.pos, 3);
|
|
4828
|
+
// 3-character operators: **=
|
|
4829
|
+
// 2-character operators
|
|
4830
|
+
var twoCharOps = ['==', '!=', '<=', '>=', '&&', '||', '??', '?.', '..', '**', '//', '::', '->'];
|
|
4831
|
+
var threeCharOps = ['...', '<<=', '>>='];
|
|
4832
|
+
if (threeCharOps.includes(threeChar)) {
|
|
4833
|
+
this.pos += 3;
|
|
4834
|
+
this.column += 3;
|
|
4835
|
+
return this.makeToken(TokenKind.Operator, threeChar, start);
|
|
4836
|
+
}
|
|
4837
|
+
if (twoCharOps.includes(twoChar)) {
|
|
4838
|
+
this.pos += 2;
|
|
4839
|
+
this.column += 2;
|
|
4840
|
+
return this.makeToken(TokenKind.Operator, twoChar, start);
|
|
4841
|
+
}
|
|
4842
|
+
// Single-character operators and separators
|
|
4843
|
+
var singleCharOps = '+-*/%^=<>!&|?.,;:#@$~';
|
|
4844
|
+
if (singleCharOps.includes(ch)) {
|
|
4845
|
+
this.pos++;
|
|
4846
|
+
this.column++;
|
|
4847
|
+
return this.makeToken(TokenKind.Operator, ch, start);
|
|
4848
|
+
}
|
|
4849
|
+
// Unrecognized character
|
|
4850
|
+
this.pos++;
|
|
4851
|
+
this.column++;
|
|
4852
|
+
return this.makeToken(TokenKind.Operator, ch, start);
|
|
4853
|
+
};
|
|
4854
|
+
_proto.isDigit = function isDigit(ch) {
|
|
4855
|
+
return ch >= '0' && ch <= '9';
|
|
4856
|
+
};
|
|
4857
|
+
_proto.isIdentStart = function isIdentStart(ch) {
|
|
4858
|
+
return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch === '_' || ch === '$';
|
|
4859
|
+
};
|
|
4860
|
+
_proto.isIdentPart = function isIdentPart(ch) {
|
|
4861
|
+
return this.isIdentStart(ch) || this.isDigit(ch);
|
|
4862
|
+
};
|
|
4863
|
+
_proto.makeToken = function makeToken(kind, value, start) {
|
|
4864
|
+
return {
|
|
4865
|
+
kind: kind,
|
|
4866
|
+
value: value,
|
|
4867
|
+
start: start,
|
|
4868
|
+
end: this.pos,
|
|
4869
|
+
line: this.startLine,
|
|
4870
|
+
column: this.startColumn
|
|
4871
|
+
};
|
|
4872
|
+
};
|
|
4873
|
+
return Lexer;
|
|
4874
|
+
}();
|
|
4875
|
+
|
|
4876
|
+
var ExprParser = /*#__PURE__*/function () {
|
|
4877
|
+
function ExprParser() {
|
|
4878
|
+
this.current = _extends({}, EOF_TOKEN);
|
|
4879
|
+
this.errors = [];
|
|
4880
|
+
this.lexer = new Lexer();
|
|
4881
|
+
}
|
|
4882
|
+
/**
|
|
4883
|
+
* Parse an expr-lang expression and return any syntax errors.
|
|
4884
|
+
* Returns empty array if the expression is valid.
|
|
4885
|
+
*/
|
|
4886
|
+
var _proto = ExprParser.prototype;
|
|
4887
|
+
_proto.parse = function parse(input) {
|
|
4888
|
+
this.errors = [];
|
|
4889
|
+
this.lexer.reset(input);
|
|
4890
|
+
this.advance();
|
|
4891
|
+
if (input.trim().length === 0) {
|
|
4892
|
+
return [];
|
|
4893
|
+
}
|
|
4894
|
+
this.parseSequenceExpression();
|
|
4895
|
+
// Check for unexpected tokens at the end
|
|
4896
|
+
if (this.curKind() !== TokenKind.EOF && this.errors.length === 0) {
|
|
4897
|
+
this.error("unexpected token \"" + this.curVal() + "\"");
|
|
4898
|
+
}
|
|
4899
|
+
return this.errors;
|
|
4900
|
+
};
|
|
4901
|
+
_proto.advance = function advance() {
|
|
4902
|
+
this.current = this.lexer.next();
|
|
4903
|
+
};
|
|
4904
|
+
_proto.curKind = function curKind() {
|
|
4905
|
+
return this.current.kind;
|
|
4906
|
+
};
|
|
4907
|
+
_proto.curVal = function curVal() {
|
|
4908
|
+
return this.current.value;
|
|
4909
|
+
};
|
|
4910
|
+
_proto.expect = function expect(kind, value) {
|
|
4911
|
+
if (this.curKind() === kind && (value === undefined || this.curVal() === value)) {
|
|
4912
|
+
this.advance();
|
|
4913
|
+
return true;
|
|
4914
|
+
}
|
|
4915
|
+
if (value) {
|
|
4916
|
+
this.error("expected \"" + value + "\" but got \"" + this.curVal() + "\"");
|
|
4917
|
+
} else {
|
|
4918
|
+
this.error("unexpected token \"" + this.curVal() + "\"");
|
|
4919
|
+
}
|
|
4920
|
+
return false;
|
|
4921
|
+
};
|
|
4922
|
+
_proto.error = function error(message) {
|
|
4923
|
+
if (this.errors.length === 0) {
|
|
4924
|
+
// Calculate end position — approximate
|
|
4925
|
+
var endLine = this.current.line;
|
|
4926
|
+
var endColumn = this.current.column + this.curVal().length;
|
|
4927
|
+
this.errors.push({
|
|
4928
|
+
message: message,
|
|
4929
|
+
startLine: this.current.line,
|
|
4930
|
+
startColumn: this.current.column,
|
|
4931
|
+
endLine: endLine,
|
|
4932
|
+
endColumn: endColumn
|
|
4933
|
+
});
|
|
4934
|
+
}
|
|
4935
|
+
};
|
|
4936
|
+
_proto.errorAt = function errorAt(token, message) {
|
|
4937
|
+
if (this.errors.length === 0) {
|
|
4938
|
+
var endColumn = token.column + token.value.length;
|
|
4939
|
+
this.errors.push({
|
|
4940
|
+
message: message,
|
|
4941
|
+
startLine: token.line,
|
|
4942
|
+
startColumn: token.column,
|
|
4943
|
+
endLine: token.line,
|
|
4944
|
+
endColumn: endColumn
|
|
4945
|
+
});
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
// ========== Parsing Functions ==========
|
|
4949
|
+
/**
|
|
4950
|
+
* parseSequenceExpression parses multiple expressions separated by semicolons.
|
|
4951
|
+
*/;
|
|
4952
|
+
_proto.parseSequenceExpression = function parseSequenceExpression() {
|
|
4953
|
+
if (this.errors.length > 0) return;
|
|
4954
|
+
this.parseExpression(0);
|
|
4955
|
+
while (this.curVal() === ';' && this.errors.length === 0) {
|
|
4956
|
+
this.advance();
|
|
4957
|
+
if (this.curKind() === TokenKind.EOF) break;
|
|
4958
|
+
this.parseExpression(0);
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
/**
|
|
4962
|
+
* parseExpression uses precedence climbing to parse binary expressions.
|
|
4963
|
+
*/;
|
|
4964
|
+
_proto.parseExpression = function parseExpression(precedence) {
|
|
4965
|
+
if (this.errors.length > 0) return;
|
|
4966
|
+
// Handle "let" at precedence 0
|
|
4967
|
+
if (precedence === 0 && this.curVal() === 'let') {
|
|
4968
|
+
this.parseVariableDeclaration();
|
|
4969
|
+
return;
|
|
4970
|
+
}
|
|
4971
|
+
// Handle "if" at precedence 0
|
|
4972
|
+
if (precedence === 0 && this.curVal() === 'if') {
|
|
4973
|
+
this.parseConditionalIf();
|
|
4974
|
+
return;
|
|
4975
|
+
}
|
|
4976
|
+
// Handle unary operators
|
|
4977
|
+
if (UNARY_OPERATORS.has(this.curVal()) && this.curKind() === TokenKind.Operator) {
|
|
4978
|
+
var unaryToken = this.current;
|
|
4979
|
+
this.advance();
|
|
4980
|
+
if (this.curKind() === TokenKind.EOF) {
|
|
4981
|
+
this.errorAt(unaryToken, 'unexpected token EOF');
|
|
4982
|
+
return;
|
|
4983
|
+
}
|
|
4984
|
+
if (this.errors.length > 0) return;
|
|
4985
|
+
this.parsePrimary();
|
|
4986
|
+
if (this.errors.length > 0) return;
|
|
4987
|
+
this.parsePostfixExpression();
|
|
4988
|
+
return;
|
|
4989
|
+
}
|
|
4990
|
+
this.parsePrimary();
|
|
4991
|
+
if (this.errors.length > 0) return;
|
|
4992
|
+
this.parsePostfixExpression();
|
|
4993
|
+
if (this.errors.length > 0) return;
|
|
4994
|
+
// Handle binary operators with precedence climbing
|
|
4995
|
+
while (this.curKind() === TokenKind.Operator && this.errors.length === 0) {
|
|
4996
|
+
var op = this.curVal();
|
|
4997
|
+
var opToken = this.current;
|
|
4998
|
+
// Handle pipe operator |
|
|
4999
|
+
if (op === '|') {
|
|
5000
|
+
this.advance(); // skip |
|
|
5001
|
+
if (this.curKind() === TokenKind.EOF) {
|
|
5002
|
+
this.errorAt(opToken, 'unexpected token EOF');
|
|
5003
|
+
return;
|
|
5004
|
+
}
|
|
5005
|
+
if (this.curKind() === TokenKind.Identifier) {
|
|
5006
|
+
this.advance(); // skip identifier
|
|
5007
|
+
this.parseArguments();
|
|
5008
|
+
if (this.curVal() === ';') break;
|
|
5009
|
+
continue;
|
|
5010
|
+
}
|
|
5011
|
+
this.error("expected identifier after pipe \"|\" but got \"" + this.curVal() + "\"");
|
|
5012
|
+
return;
|
|
5013
|
+
}
|
|
5014
|
+
// Handle semicolon — end of this expression
|
|
5015
|
+
if (op === ';') break;
|
|
5016
|
+
// Check precedence
|
|
5017
|
+
var opPrec = OPERATOR_PRECEDENCE[op];
|
|
5018
|
+
if (opPrec === undefined || opPrec < precedence) break;
|
|
5019
|
+
// Handle "not" prefix for "not in", "not contains", etc.
|
|
5020
|
+
if (op === 'not') {
|
|
5021
|
+
this.advance();
|
|
5022
|
+
var nextOp = this.curVal();
|
|
5023
|
+
var negatedPrec = OPERATOR_PRECEDENCE[nextOp];
|
|
5024
|
+
if (negatedPrec !== undefined && negatedPrec >= precedence) {
|
|
5025
|
+
this.advance();
|
|
5026
|
+
this.parseExpression(negatedPrec + 1);
|
|
5027
|
+
continue;
|
|
5028
|
+
}
|
|
5029
|
+
this.error("unexpected token \"" + nextOp + "\" after \"not\"");
|
|
5030
|
+
return;
|
|
5031
|
+
}
|
|
5032
|
+
// Handle chained comparisons: a < b < c => (a < b) && (b < c)
|
|
5033
|
+
if (COMPARISON_OPERATORS.has(op)) {
|
|
5034
|
+
this.advance();
|
|
5035
|
+
if (this.curKind() === TokenKind.EOF) {
|
|
5036
|
+
this.errorAt(opToken, 'unexpected token EOF');
|
|
5037
|
+
return;
|
|
5038
|
+
}
|
|
5039
|
+
this.parseExpression(opPrec + 1);
|
|
5040
|
+
// Keep parsing chained comparisons
|
|
5041
|
+
while (this.curKind() === TokenKind.Operator && COMPARISON_OPERATORS.has(this.curVal()) && this.errors.length === 0) {
|
|
5042
|
+
var chainOpToken = this.current;
|
|
5043
|
+
this.advance();
|
|
5044
|
+
if (this.curKind() === TokenKind.EOF) {
|
|
5045
|
+
this.errorAt(chainOpToken, 'unexpected token EOF');
|
|
5046
|
+
return;
|
|
5047
|
+
}
|
|
5048
|
+
this.parseExpression((OPERATOR_PRECEDENCE[this.curVal()] || 0) + 1);
|
|
5049
|
+
}
|
|
5050
|
+
continue;
|
|
5051
|
+
}
|
|
5052
|
+
this.advance();
|
|
5053
|
+
if (this.curKind() === TokenKind.EOF) {
|
|
5054
|
+
this.errorAt(opToken, 'unexpected token EOF');
|
|
5055
|
+
return;
|
|
5056
|
+
}
|
|
5057
|
+
// Right-associative operators bind to the right
|
|
5058
|
+
// For simplicity, parse with same precedence for right-assoc
|
|
5059
|
+
this.parseExpression(opPrec + 1);
|
|
5060
|
+
// Handle ternary ? :
|
|
5061
|
+
if (precedence === 0 && this.curVal() === '?') {
|
|
5062
|
+
this.parseConditional();
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
// Handle ternary ? : at precedence 0
|
|
5066
|
+
if (precedence === 0 && this.curVal() === '?') {
|
|
5067
|
+
this.parseConditional();
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
/**
|
|
5071
|
+
* parsePrimary handles unary operators, parentheses, and the #/. pointer prefix.
|
|
5072
|
+
*/;
|
|
5073
|
+
_proto.parsePrimary = function parsePrimary() {
|
|
5074
|
+
if (this.errors.length > 0) return;
|
|
5075
|
+
// Unary operators are handled in parseExpression
|
|
5076
|
+
// Parenthesized expression
|
|
5077
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === '(') {
|
|
5078
|
+
this.advance(); // skip (
|
|
5079
|
+
this.parseSequenceExpression();
|
|
5080
|
+
if (!this.expect(TokenKind.Bracket, ')')) {
|
|
5081
|
+
return;
|
|
5082
|
+
}
|
|
5083
|
+
return;
|
|
5084
|
+
}
|
|
5085
|
+
// Handle # or . prefix (in predicates)
|
|
5086
|
+
if ((this.curVal() === '#' || this.curVal() === '.') && this.curKind() === TokenKind.Operator) {
|
|
5087
|
+
this.advance();
|
|
5088
|
+
if (this.curKind() === TokenKind.Identifier) {
|
|
5089
|
+
this.advance();
|
|
5090
|
+
}
|
|
5091
|
+
this.parsePostfixExpression();
|
|
5092
|
+
return;
|
|
5093
|
+
}
|
|
5094
|
+
this.parseSecondary();
|
|
5095
|
+
}
|
|
5096
|
+
/**
|
|
5097
|
+
* parseSecondary handles identifiers, literals, arrays, and maps.
|
|
5098
|
+
*/;
|
|
5099
|
+
_proto.parseSecondary = function parseSecondary() {
|
|
5100
|
+
if (this.errors.length > 0) return;
|
|
5101
|
+
var token = this.current;
|
|
5102
|
+
switch (token.kind) {
|
|
5103
|
+
case TokenKind.Identifier:
|
|
5104
|
+
{
|
|
5105
|
+
this.advance();
|
|
5106
|
+
// Check for function call
|
|
5107
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === '(') {
|
|
5108
|
+
this.parseArguments();
|
|
5109
|
+
}
|
|
5110
|
+
break;
|
|
5111
|
+
}
|
|
5112
|
+
case TokenKind.Number:
|
|
5113
|
+
{
|
|
5114
|
+
this.advance();
|
|
5115
|
+
break;
|
|
5116
|
+
}
|
|
5117
|
+
case TokenKind.String:
|
|
5118
|
+
{
|
|
5119
|
+
this.advance();
|
|
5120
|
+
break;
|
|
5121
|
+
}
|
|
5122
|
+
case TokenKind.Bracket:
|
|
5123
|
+
{
|
|
5124
|
+
if (token.value === '[') {
|
|
5125
|
+
this.parseArrayExpression();
|
|
5126
|
+
} else if (token.value === '{') {
|
|
5127
|
+
this.parseMapExpression();
|
|
5128
|
+
} else {
|
|
5129
|
+
this.error("unexpected token \"" + token.value + "\"");
|
|
5130
|
+
}
|
|
5131
|
+
break;
|
|
5132
|
+
}
|
|
5133
|
+
default:
|
|
5134
|
+
this.error("unexpected token \"" + token.value + "\"");
|
|
5135
|
+
break;
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
/**
|
|
5139
|
+
* parsePostfixExpression handles .member, ?.member, [index], [from:to], and calls after the primary.
|
|
5140
|
+
*/;
|
|
5141
|
+
_proto.parsePostfixExpression = function parsePostfixExpression() {
|
|
5142
|
+
while (this.errors.length === 0) {
|
|
5143
|
+
var token = this.current;
|
|
5144
|
+
// .member or ?.member
|
|
5145
|
+
if (token.kind === TokenKind.Operator && (token.value === '.' || token.value === '?.')) {
|
|
5146
|
+
this.advance();
|
|
5147
|
+
// After . or ?., expect an identifier (or operator like "not" that can be a method name)
|
|
5148
|
+
if (this.curKind() === TokenKind.Identifier || this.curKind() === TokenKind.Operator) {
|
|
5149
|
+
this.advance();
|
|
5150
|
+
// Check for method call: obj.method()
|
|
5151
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === '(') {
|
|
5152
|
+
this.parseArguments();
|
|
5153
|
+
}
|
|
5154
|
+
} else if (this.curKind() === TokenKind.Bracket && this.curVal() === '[' && token.value === '?.') {
|
|
5155
|
+
// obj?[index] — handle bracket after ?.
|
|
5156
|
+
this.advance();
|
|
5157
|
+
this.parseExpression(0);
|
|
5158
|
+
this.expect(TokenKind.Bracket, ']');
|
|
5159
|
+
} else {
|
|
5160
|
+
this.error("expected property name after \"" + token.value + "\" but got \"" + this.curVal() + "\"");
|
|
5161
|
+
return;
|
|
5162
|
+
}
|
|
5163
|
+
continue;
|
|
5164
|
+
}
|
|
5165
|
+
// [index] or [from:to]
|
|
5166
|
+
if (token.kind === TokenKind.Bracket && token.value === '[') {
|
|
5167
|
+
this.advance();
|
|
5168
|
+
// Check for slice: [:] or [:to]
|
|
5169
|
+
if (this.curKind() === TokenKind.Operator && this.curVal() === ':') {
|
|
5170
|
+
this.advance();
|
|
5171
|
+
if (this.curKind() !== TokenKind.Bracket || this.curVal() !== ']') {
|
|
5172
|
+
this.parseExpression(0);
|
|
5173
|
+
}
|
|
5174
|
+
this.expect(TokenKind.Bracket, ']');
|
|
5175
|
+
} else {
|
|
5176
|
+
// Index expression
|
|
5177
|
+
this.parseExpression(0);
|
|
5178
|
+
// Check for slice: [from:]
|
|
5179
|
+
if (this.curKind() === TokenKind.Operator && this.curVal() === ':') {
|
|
5180
|
+
this.advance();
|
|
5181
|
+
if (this.curKind() !== TokenKind.Bracket || this.curVal() !== ']') {
|
|
5182
|
+
this.parseExpression(0);
|
|
5183
|
+
}
|
|
5184
|
+
}
|
|
5185
|
+
this.expect(TokenKind.Bracket, ']');
|
|
5186
|
+
}
|
|
5187
|
+
continue;
|
|
5188
|
+
}
|
|
5189
|
+
// Function call after member access
|
|
5190
|
+
if (token.kind === TokenKind.Bracket && token.value === '(') {
|
|
5191
|
+
this.parseArguments();
|
|
5192
|
+
continue;
|
|
5193
|
+
}
|
|
5194
|
+
break;
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
/**
|
|
5198
|
+
* parseVariableDeclaration parses "let name = value; rest"
|
|
5199
|
+
*/;
|
|
5200
|
+
_proto.parseVariableDeclaration = function parseVariableDeclaration() {
|
|
5201
|
+
this.expect(TokenKind.Operator, 'let');
|
|
5202
|
+
if (this.errors.length > 0) return;
|
|
5203
|
+
if (this.curKind() !== TokenKind.Identifier) {
|
|
5204
|
+
this.error("expected variable name after \"let\" but got \"" + this.curVal() + "\"");
|
|
5205
|
+
return;
|
|
5206
|
+
}
|
|
5207
|
+
this.advance(); // skip variable name
|
|
5208
|
+
if (!this.expect(TokenKind.Operator, '=')) return;
|
|
5209
|
+
this.parseExpression(0);
|
|
5210
|
+
if (this.errors.length > 0) return;
|
|
5211
|
+
// Optional semicolon after value
|
|
5212
|
+
if (this.curVal() === ';') {
|
|
5213
|
+
this.advance();
|
|
5214
|
+
if (this.curKind() !== TokenKind.EOF) {
|
|
5215
|
+
this.parseSequenceExpression();
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
/**
|
|
5220
|
+
* parseConditionalIf parses "if expr { expr1 } else { expr2 }" or "if expr { expr1 } else if ..."
|
|
5221
|
+
*/;
|
|
5222
|
+
_proto.parseConditionalIf = function parseConditionalIf() {
|
|
5223
|
+
this.advance(); // skip 'if'
|
|
5224
|
+
if (this.errors.length > 0) return;
|
|
5225
|
+
this.parseExpression(0);
|
|
5226
|
+
if (this.errors.length > 0) return;
|
|
5227
|
+
if (!this.expect(TokenKind.Bracket, '{')) return;
|
|
5228
|
+
this.parseSequenceExpression();
|
|
5229
|
+
if (this.errors.length > 0) return;
|
|
5230
|
+
if (!this.expect(TokenKind.Bracket, '}')) return;
|
|
5231
|
+
if (!this.expect(TokenKind.Operator, 'else')) return;
|
|
5232
|
+
// Nested if
|
|
5233
|
+
if (this.curVal() === 'if') {
|
|
5234
|
+
this.parseConditionalIf();
|
|
5235
|
+
return;
|
|
5236
|
+
}
|
|
5237
|
+
if (!this.expect(TokenKind.Bracket, '{')) return;
|
|
5238
|
+
this.parseSequenceExpression();
|
|
5239
|
+
if (this.errors.length > 0) return;
|
|
5240
|
+
this.expect(TokenKind.Bracket, '}');
|
|
5241
|
+
}
|
|
5242
|
+
/**
|
|
5243
|
+
* parseConditional parses "expr ? expr1 : expr2" or "expr ?: expr2"
|
|
5244
|
+
*/;
|
|
5245
|
+
_proto.parseConditional = function parseConditional() {
|
|
5246
|
+
if (this.curVal() !== '?') return;
|
|
5247
|
+
this.advance(); // skip ?
|
|
5248
|
+
// Elvis operator ?:
|
|
5249
|
+
if (this.curVal() === ':') {
|
|
5250
|
+
this.advance();
|
|
5251
|
+
this.parseExpression(0);
|
|
5252
|
+
return;
|
|
5253
|
+
}
|
|
5254
|
+
this.parseExpression(0);
|
|
5255
|
+
if (this.errors.length > 0) return;
|
|
5256
|
+
if (!this.expect(TokenKind.Operator, ':')) return;
|
|
5257
|
+
this.parseExpression(0);
|
|
5258
|
+
}
|
|
5259
|
+
/**
|
|
5260
|
+
* parseArguments parses function call arguments: (arg1, arg2, ...)
|
|
5261
|
+
*/;
|
|
5262
|
+
_proto.parseArguments = function parseArguments() {
|
|
5263
|
+
if (!this.expect(TokenKind.Bracket, '(')) return;
|
|
5264
|
+
while (this.curKind() !== TokenKind.Bracket || this.curVal() !== ')') {
|
|
5265
|
+
if (this.curKind() === TokenKind.EOF) {
|
|
5266
|
+
this.error('unexpected end of expression, expected ")"');
|
|
5267
|
+
return;
|
|
5268
|
+
}
|
|
5269
|
+
if (this.curVal() === ',') {
|
|
5270
|
+
this.advance();
|
|
5271
|
+
continue;
|
|
5272
|
+
}
|
|
5273
|
+
// Check for predicate { ... } argument
|
|
5274
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === '{') {
|
|
5275
|
+
this.advance();
|
|
5276
|
+
this.parseSequenceExpression();
|
|
5277
|
+
this.expect(TokenKind.Bracket, '}');
|
|
5278
|
+
} else {
|
|
5279
|
+
this.parseExpression(0);
|
|
5280
|
+
}
|
|
5281
|
+
if (this.errors.length > 0) return;
|
|
5282
|
+
if (this.curVal() === ',') {
|
|
5283
|
+
this.advance();
|
|
5284
|
+
// Allow trailing comma
|
|
5285
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === ')') {
|
|
5286
|
+
break;
|
|
5287
|
+
}
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
this.expect(TokenKind.Bracket, ')');
|
|
5291
|
+
}
|
|
5292
|
+
/**
|
|
5293
|
+
* parseArrayExpression parses "[elem1, elem2, ...]"
|
|
5294
|
+
*/;
|
|
5295
|
+
_proto.parseArrayExpression = function parseArrayExpression() {
|
|
5296
|
+
this.expect(TokenKind.Bracket, '[');
|
|
5297
|
+
if (this.errors.length > 0) return;
|
|
5298
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === ']') {
|
|
5299
|
+
this.advance();
|
|
5300
|
+
return;
|
|
5301
|
+
}
|
|
5302
|
+
while (this.errors.length === 0) {
|
|
5303
|
+
this.parseExpression(0);
|
|
5304
|
+
if (this.errors.length > 0) return;
|
|
5305
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === ']') {
|
|
5306
|
+
break;
|
|
5307
|
+
}
|
|
5308
|
+
if (this.curVal() === ',') {
|
|
5309
|
+
this.advance();
|
|
5310
|
+
// Allow trailing comma
|
|
5311
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === ']') {
|
|
5312
|
+
break;
|
|
5313
|
+
}
|
|
5314
|
+
continue;
|
|
5315
|
+
}
|
|
5316
|
+
this.error("expected \",\" or \"]\" but got \"" + this.curVal() + "\"");
|
|
5317
|
+
return;
|
|
5318
|
+
}
|
|
5319
|
+
this.expect(TokenKind.Bracket, ']');
|
|
5320
|
+
}
|
|
5321
|
+
/**
|
|
5322
|
+
* parseMapExpression parses "{key: value, ...}"
|
|
5323
|
+
*/;
|
|
5324
|
+
_proto.parseMapExpression = function parseMapExpression() {
|
|
5325
|
+
this.expect(TokenKind.Bracket, '{');
|
|
5326
|
+
if (this.errors.length > 0) return;
|
|
5327
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === '}') {
|
|
5328
|
+
this.advance();
|
|
5329
|
+
return;
|
|
5330
|
+
}
|
|
5331
|
+
while (this.errors.length === 0) {
|
|
5332
|
+
// Key: identifier, string, number, or parenthesized expression
|
|
5333
|
+
if (this.curKind() === TokenKind.Identifier || this.curKind() === TokenKind.String || this.curKind() === TokenKind.Number) {
|
|
5334
|
+
this.advance();
|
|
5335
|
+
} else if (this.curKind() === TokenKind.Bracket && this.curVal() === '(') {
|
|
5336
|
+
this.advance();
|
|
5337
|
+
this.parseExpression(0);
|
|
5338
|
+
this.expect(TokenKind.Bracket, ')');
|
|
5339
|
+
} else {
|
|
5340
|
+
this.error("map key must be a string, number, identifier, or parenthesized expression, got \"" + this.curVal() + "\"");
|
|
5341
|
+
return;
|
|
5342
|
+
}
|
|
5343
|
+
if (!this.expect(TokenKind.Operator, ':')) return;
|
|
5344
|
+
this.parseExpression(0);
|
|
5345
|
+
if (this.errors.length > 0) return;
|
|
5346
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === '}') {
|
|
5347
|
+
break;
|
|
5348
|
+
}
|
|
5349
|
+
if (this.curVal() === ',') {
|
|
5350
|
+
this.advance();
|
|
5351
|
+
if (this.curKind() === TokenKind.Bracket && this.curVal() === '}') {
|
|
5352
|
+
break;
|
|
5353
|
+
}
|
|
5354
|
+
continue;
|
|
5355
|
+
}
|
|
5356
|
+
this.error("expected \",\" or \"}\" but got \"" + this.curVal() + "\"");
|
|
5357
|
+
return;
|
|
5358
|
+
}
|
|
5359
|
+
this.expect(TokenKind.Bracket, '}');
|
|
5360
|
+
};
|
|
5361
|
+
return ExprParser;
|
|
5362
|
+
}();
|
|
5363
|
+
|
|
5364
|
+
var EXPR_LANG_ID = 'expr';
|
|
5365
|
+
var parser = /*#__PURE__*/new ExprParser();
|
|
5366
|
+
/**
|
|
5367
|
+
* Validate an expr-lang expression and return Monaco editor markers.
|
|
5368
|
+
* Uses the recursive descent parser for syntax errors,
|
|
5369
|
+
* plus regex-based checks for structural issues like unbalanced quotes/brackets.
|
|
5370
|
+
*/
|
|
5371
|
+
var validateExpr = function validateExpr(expr) {
|
|
5372
|
+
var markers = [];
|
|
5373
|
+
if (!expr || expr.trim().length === 0) {
|
|
5374
|
+
return markers;
|
|
5375
|
+
}
|
|
5376
|
+
// 1. Parser-based syntax validation
|
|
5377
|
+
var parseErrors = parser.parse(expr);
|
|
5378
|
+
for (var _iterator = _createForOfIteratorHelperLoose(parseErrors), _step; !(_step = _iterator()).done;) {
|
|
5379
|
+
var err = _step.value;
|
|
5380
|
+
markers.push({
|
|
5381
|
+
severity: MarkerSeverity.Error,
|
|
5382
|
+
startLineNumber: err.startLine,
|
|
5383
|
+
startColumn: err.startColumn,
|
|
5384
|
+
endLineNumber: err.endLine,
|
|
5385
|
+
endColumn: err.endColumn,
|
|
5386
|
+
message: err.message
|
|
5387
|
+
});
|
|
5388
|
+
}
|
|
5389
|
+
// 2. Line-level structural checks (fallback for issues the parser might miss)
|
|
5390
|
+
var structuralMarkers = validateLineLevel(expr);
|
|
5391
|
+
markers.push.apply(markers, structuralMarkers);
|
|
5392
|
+
return markers;
|
|
5393
|
+
};
|
|
5394
|
+
/**
|
|
5395
|
+
* Basic line-level validation for quotes and bracket balancing.
|
|
5396
|
+
*/
|
|
5397
|
+
function validateLineLevel(expr) {
|
|
5398
|
+
var markers = [];
|
|
5399
|
+
var lines = expr.split('\n');
|
|
5400
|
+
var inBlockComment = false;
|
|
5401
|
+
lines.forEach(function (line, index) {
|
|
5402
|
+
var lineNumber = index + 1;
|
|
5403
|
+
// Track block comment state
|
|
5404
|
+
if (!inBlockComment) {
|
|
5405
|
+
var blockCommentStart = line.indexOf('/*');
|
|
5406
|
+
if (blockCommentStart !== -1) {
|
|
5407
|
+
var blockCommentEnd = line.indexOf('*/', blockCommentStart + 2);
|
|
5408
|
+
if (blockCommentEnd === -1) {
|
|
5409
|
+
inBlockComment = true;
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
} else {
|
|
5413
|
+
var _blockCommentEnd = line.indexOf('*/');
|
|
5414
|
+
if (_blockCommentEnd !== -1) {
|
|
5415
|
+
inBlockComment = false;
|
|
5416
|
+
}
|
|
5417
|
+
if (inBlockComment || line.trim().startsWith('/*')) {
|
|
5418
|
+
return;
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
var trimmedLine = line.trim();
|
|
5422
|
+
if (trimmedLine.startsWith('//') || trimmedLine.startsWith('/*')) {
|
|
5423
|
+
return;
|
|
5424
|
+
}
|
|
5425
|
+
// Check for unclosed quotes
|
|
5426
|
+
var singleQuotes = countQuotesOutsideBlockComments(line);
|
|
5427
|
+
if (singleQuotes % 2 !== 0) {
|
|
5428
|
+
markers.push({
|
|
5429
|
+
severity: MarkerSeverity.Warning,
|
|
5430
|
+
startLineNumber: lineNumber,
|
|
5431
|
+
startColumn: 1,
|
|
5432
|
+
endLineNumber: lineNumber,
|
|
5433
|
+
endColumn: line.length + 1,
|
|
5434
|
+
message: "Unclosed single quote '",
|
|
5435
|
+
source: EXPR_LANG_ID
|
|
5436
|
+
});
|
|
5437
|
+
}
|
|
5438
|
+
var doubleQuotes = countDoubleQuotesOutsideBlockComments(line);
|
|
5439
|
+
if (doubleQuotes % 2 !== 0) {
|
|
5440
|
+
markers.push({
|
|
5441
|
+
severity: MarkerSeverity.Warning,
|
|
5442
|
+
startLineNumber: lineNumber,
|
|
5443
|
+
startColumn: 1,
|
|
5444
|
+
endLineNumber: lineNumber,
|
|
5445
|
+
endColumn: line.length + 1,
|
|
5446
|
+
message: 'Unclosed double quote "',
|
|
5447
|
+
source: EXPR_LANG_ID
|
|
5448
|
+
});
|
|
5449
|
+
}
|
|
5450
|
+
var backticks = (line.match(/`/g) || []).length;
|
|
5451
|
+
if (backticks % 2 !== 0) {
|
|
5452
|
+
markers.push({
|
|
5453
|
+
severity: MarkerSeverity.Warning,
|
|
5454
|
+
startLineNumber: lineNumber,
|
|
5455
|
+
startColumn: 1,
|
|
5456
|
+
endLineNumber: lineNumber,
|
|
5457
|
+
endColumn: line.length + 1,
|
|
5458
|
+
message: 'Unclosed backtick `',
|
|
5459
|
+
source: EXPR_LANG_ID
|
|
5460
|
+
});
|
|
5461
|
+
}
|
|
5462
|
+
// Check for unbalanced brackets
|
|
5463
|
+
var openParens = (line.match(/\(/g) || []).length;
|
|
5464
|
+
var closeParens = (line.match(/\)/g) || []).length;
|
|
5465
|
+
if (openParens > closeParens) {
|
|
5466
|
+
markers.push({
|
|
5467
|
+
severity: MarkerSeverity.Warning,
|
|
5468
|
+
startLineNumber: lineNumber,
|
|
5469
|
+
startColumn: 1,
|
|
5470
|
+
endLineNumber: lineNumber,
|
|
5471
|
+
endColumn: line.length + 1,
|
|
5472
|
+
message: 'Unmatched opening parenthesis',
|
|
5473
|
+
source: EXPR_LANG_ID
|
|
5474
|
+
});
|
|
5475
|
+
} else if (closeParens > openParens) {
|
|
5476
|
+
markers.push({
|
|
5477
|
+
severity: MarkerSeverity.Warning,
|
|
5478
|
+
startLineNumber: lineNumber,
|
|
5479
|
+
startColumn: 1,
|
|
5480
|
+
endLineNumber: lineNumber,
|
|
5481
|
+
endColumn: line.length + 1,
|
|
5482
|
+
message: 'Unmatched closing parenthesis',
|
|
5483
|
+
source: EXPR_LANG_ID
|
|
5484
|
+
});
|
|
5485
|
+
}
|
|
5486
|
+
var openBrackets = (line.match(/\[/g) || []).length;
|
|
5487
|
+
var closeBrackets = (line.match(/\]/g) || []).length;
|
|
5488
|
+
if (openBrackets > closeBrackets) {
|
|
5489
|
+
markers.push({
|
|
5490
|
+
severity: MarkerSeverity.Warning,
|
|
5491
|
+
startLineNumber: lineNumber,
|
|
5492
|
+
startColumn: 1,
|
|
5493
|
+
endLineNumber: lineNumber,
|
|
5494
|
+
endColumn: line.length + 1,
|
|
5495
|
+
message: 'Unmatched opening bracket',
|
|
5496
|
+
source: EXPR_LANG_ID
|
|
5497
|
+
});
|
|
5498
|
+
} else if (closeBrackets > openBrackets) {
|
|
5499
|
+
markers.push({
|
|
5500
|
+
severity: MarkerSeverity.Warning,
|
|
5501
|
+
startLineNumber: lineNumber,
|
|
5502
|
+
startColumn: 1,
|
|
5503
|
+
endLineNumber: lineNumber,
|
|
5504
|
+
endColumn: line.length + 1,
|
|
5505
|
+
message: 'Unmatched closing bracket',
|
|
5506
|
+
source: EXPR_LANG_ID
|
|
5507
|
+
});
|
|
5508
|
+
}
|
|
5509
|
+
var openBraces = (line.match(/\{/g) || []).length;
|
|
5510
|
+
var closeBraces = (line.match(/\}/g) || []).length;
|
|
5511
|
+
if (openBraces > closeBraces) {
|
|
5512
|
+
markers.push({
|
|
5513
|
+
severity: MarkerSeverity.Warning,
|
|
5514
|
+
startLineNumber: lineNumber,
|
|
5515
|
+
startColumn: 1,
|
|
5516
|
+
endLineNumber: lineNumber,
|
|
5517
|
+
endColumn: line.length + 1,
|
|
5518
|
+
message: 'Unmatched opening curly brace',
|
|
5519
|
+
source: EXPR_LANG_ID
|
|
5520
|
+
});
|
|
5521
|
+
} else if (closeBraces > openBraces) {
|
|
5522
|
+
markers.push({
|
|
5523
|
+
severity: MarkerSeverity.Warning,
|
|
5524
|
+
startLineNumber: lineNumber,
|
|
5525
|
+
startColumn: 1,
|
|
5526
|
+
endLineNumber: lineNumber,
|
|
5527
|
+
endColumn: line.length + 1,
|
|
5528
|
+
message: 'Unmatched closing curly brace',
|
|
5529
|
+
source: EXPR_LANG_ID
|
|
5530
|
+
});
|
|
5531
|
+
}
|
|
5532
|
+
});
|
|
5533
|
+
return markers;
|
|
5534
|
+
}
|
|
5535
|
+
function countQuotesOutsideBlockComments(line) {
|
|
5536
|
+
var withoutBlockComments = line.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
5537
|
+
return (withoutBlockComments.match(/'/g) || []).length;
|
|
5538
|
+
}
|
|
5539
|
+
function countDoubleQuotesOutsideBlockComments(line) {
|
|
5540
|
+
var withoutBlockComments = line.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
5541
|
+
return (withoutBlockComments.match(/"/g) || []).length;
|
|
5542
|
+
}
|
|
5543
|
+
|
|
5544
|
+
var _templateObject$3, _templateObject2$3;
|
|
5545
|
+
var EXPR_LANG_ID$1 = 'expr';
|
|
5546
|
+
var SIZE_MAP$3 = {
|
|
5547
|
+
small: {
|
|
5548
|
+
className: 'ant-input-sm',
|
|
5549
|
+
top: 1,
|
|
5550
|
+
bottom: 1,
|
|
5551
|
+
minHeight: 24
|
|
5552
|
+
},
|
|
5553
|
+
middle: {
|
|
5554
|
+
className: 'ant-input-md',
|
|
5555
|
+
top: 1,
|
|
5556
|
+
bottom: 1,
|
|
5557
|
+
minHeight: 32
|
|
5558
|
+
},
|
|
5559
|
+
large: {
|
|
5560
|
+
className: 'ant-input-lg',
|
|
5561
|
+
top: 3,
|
|
5562
|
+
bottom: 2,
|
|
5563
|
+
minHeight: 40
|
|
5564
|
+
}
|
|
5565
|
+
};
|
|
5566
|
+
var themeMap$3 = {
|
|
5567
|
+
light: 'expr-light',
|
|
5568
|
+
dark: 'expr-dark'
|
|
5569
|
+
};
|
|
5570
|
+
var containerDisabledClassName$3 = /*#__PURE__*/css(_templateObject$3 || (_templateObject$3 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n .monaco-editor {\n user-select: none;\n pointer-events: none;\n }\n"])));
|
|
5571
|
+
var containerReadOnlyClassName$3 = /*#__PURE__*/css(_templateObject2$3 || (_templateObject2$3 = /*#__PURE__*/_taggedTemplateLiteralLoose(["\n .monaco-editor .cursors-layer > .cursor {\n opacity: 0 !important;\n }\n"])));
|
|
5572
|
+
function ExprEditor(props) {
|
|
5573
|
+
var id = v4();
|
|
5574
|
+
var className = props.className,
|
|
5575
|
+
maxHeight = props.maxHeight,
|
|
5576
|
+
fontSize = props.fontSize,
|
|
5577
|
+
_props$size = props.size,
|
|
5578
|
+
size = _props$size === void 0 ? 'middle' : _props$size,
|
|
5579
|
+
_props$theme = props.theme,
|
|
5580
|
+
theme = _props$theme === void 0 ? 'light' : _props$theme,
|
|
5581
|
+
_props$value = props.value,
|
|
5582
|
+
value = _props$value === void 0 ? '' : _props$value,
|
|
5583
|
+
placeholder = props.placeholder,
|
|
5584
|
+
_props$enableAutocomp = props.enableAutocomplete,
|
|
5585
|
+
enableAutocomplete = _props$enableAutocomp === void 0 ? true : _props$enableAutocomp,
|
|
5586
|
+
_props$readOnly = props.readOnly,
|
|
5587
|
+
readOnly = _props$readOnly === void 0 ? false : _props$readOnly,
|
|
5588
|
+
_props$disabled = props.disabled,
|
|
5589
|
+
disabled = _props$disabled === void 0 ? false : _props$disabled,
|
|
5590
|
+
onChange = props.onChange,
|
|
5591
|
+
onEnter = props.onEnter,
|
|
5592
|
+
onBlur = props.onBlur,
|
|
5593
|
+
onFocus = props.onFocus,
|
|
5594
|
+
editorDidMount = props.editorDidMount;
|
|
5595
|
+
var containerRef = useRef(null);
|
|
5596
|
+
var editorRef = useRef(null);
|
|
5597
|
+
var modelRef = useRef(null);
|
|
5598
|
+
var disposablesRef = useRef([]);
|
|
5599
|
+
useEffect(function () {
|
|
5600
|
+
// Register language
|
|
5601
|
+
if (!languages.getLanguages().some(function (lang) {
|
|
5602
|
+
return lang.id === EXPR_LANG_ID$1;
|
|
5603
|
+
})) {
|
|
5604
|
+
languages.register({
|
|
5605
|
+
id: EXPR_LANG_ID$1
|
|
5606
|
+
});
|
|
5607
|
+
languages.setMonarchTokensProvider(EXPR_LANG_ID$1, language$3);
|
|
5608
|
+
languages.setLanguageConfiguration(EXPR_LANG_ID$1, languageConfiguration$3);
|
|
5609
|
+
}
|
|
5610
|
+
// Register completion provider
|
|
5611
|
+
if (enableAutocomplete) {
|
|
5612
|
+
var disposable = languages.registerCompletionItemProvider(EXPR_LANG_ID$1, getExprCompletionProvider());
|
|
5613
|
+
disposablesRef.current.push(disposable);
|
|
5614
|
+
}
|
|
5615
|
+
return function () {
|
|
5616
|
+
disposablesRef.current.forEach(function (disposable) {
|
|
5617
|
+
return disposable.dispose();
|
|
5618
|
+
});
|
|
5619
|
+
disposablesRef.current = [];
|
|
5620
|
+
};
|
|
5621
|
+
}, [enableAutocomplete]);
|
|
5622
|
+
var handleEditorMount = function handleEditorMount(editor$1) {
|
|
5623
|
+
editorRef.current = editor$1;
|
|
5624
|
+
modelRef.current = editor$1.getModel();
|
|
5625
|
+
editor.defineTheme('expr-light', {
|
|
5626
|
+
base: 'vs',
|
|
5627
|
+
inherit: true,
|
|
5628
|
+
rules: [],
|
|
5629
|
+
colors: {
|
|
5630
|
+
'editor.background': '#00000000',
|
|
5631
|
+
focusBorder: '#00000000'
|
|
5632
|
+
}
|
|
5633
|
+
});
|
|
5634
|
+
editor.defineTheme('expr-dark', {
|
|
5635
|
+
base: 'vs-dark',
|
|
5636
|
+
inherit: true,
|
|
5637
|
+
rules: [],
|
|
5638
|
+
colors: {
|
|
5639
|
+
'editor.background': '#00000000',
|
|
5640
|
+
focusBorder: '#00000000'
|
|
5641
|
+
}
|
|
5642
|
+
});
|
|
5643
|
+
var isEditorFocused = editor$1.createContextKey('isEditorFocused' + id, false);
|
|
5644
|
+
editor$1.onDidBlurEditorWidget(function () {
|
|
5645
|
+
isEditorFocused.set(false);
|
|
5646
|
+
onBlur == null || onBlur(editor$1.getValue());
|
|
5647
|
+
var position = editor$1.getPosition();
|
|
5648
|
+
if (position) {
|
|
5649
|
+
var newSelection = new Selection(position.lineNumber, position.column, position.lineNumber, position.column);
|
|
5650
|
+
editor$1.setSelection(newSelection);
|
|
5651
|
+
}
|
|
5652
|
+
});
|
|
5653
|
+
editor$1.onDidFocusEditorText(function () {
|
|
5654
|
+
isEditorFocused.set(true);
|
|
5655
|
+
onFocus == null || onFocus(editor$1.getValue());
|
|
5656
|
+
});
|
|
5657
|
+
// Auto-height
|
|
5658
|
+
var updateElementHeight = function updateElementHeight() {
|
|
5659
|
+
var containerDiv = containerRef.current;
|
|
5660
|
+
if (containerDiv !== null) {
|
|
5661
|
+
var pixelHeight = editor$1.getContentHeight();
|
|
5662
|
+
containerDiv.style.minHeight = pixelHeight + "px";
|
|
5663
|
+
containerDiv.style.width = '100%';
|
|
5664
|
+
var pixelWidth = containerDiv.clientWidth;
|
|
5665
|
+
editor$1.layout({
|
|
5666
|
+
width: pixelWidth,
|
|
5667
|
+
height: pixelHeight
|
|
5668
|
+
});
|
|
5669
|
+
}
|
|
5670
|
+
};
|
|
5671
|
+
editor$1.onDidContentSizeChange(updateElementHeight);
|
|
5672
|
+
updateElementHeight();
|
|
5673
|
+
// Disable search box
|
|
5674
|
+
editor.addKeybindingRule({
|
|
5675
|
+
keybinding: KeyMod.CtrlCmd | KeyCode.KeyF,
|
|
5676
|
+
command: null
|
|
5677
|
+
});
|
|
5678
|
+
// Shift+Enter for newline
|
|
5679
|
+
editor$1.addCommand(KeyMod.Shift | KeyCode.Enter, function () {
|
|
5680
|
+
var position = editor$1.getPosition();
|
|
5681
|
+
if (position) {
|
|
5682
|
+
editor$1.executeEdits('shift-enter', [{
|
|
5683
|
+
range: new Range(position.lineNumber, position.column, position.lineNumber, position.column),
|
|
5684
|
+
text: '\n'
|
|
5685
|
+
}]);
|
|
5686
|
+
editor$1.setPosition({
|
|
5687
|
+
lineNumber: position.lineNumber + 1,
|
|
5688
|
+
column: 1
|
|
5689
|
+
});
|
|
5690
|
+
}
|
|
5691
|
+
}, 'isEditorFocused' + id);
|
|
5692
|
+
// Prevent default Enter
|
|
5693
|
+
editor.addKeybindingRule({
|
|
5694
|
+
keybinding: KeyCode.Enter,
|
|
5695
|
+
command: '-',
|
|
5696
|
+
when: '!suggestWidgetVisible'
|
|
5697
|
+
});
|
|
5698
|
+
// Custom Enter handler
|
|
5699
|
+
editor$1.addCommand(KeyCode.Enter, function () {
|
|
5700
|
+
onEnter == null || onEnter(editor$1.getValue());
|
|
5701
|
+
}, '!suggestWidgetVisible && isEditorFocused' + id);
|
|
5702
|
+
// Setup validation on content change using decorations (no marker hover clutter)
|
|
5703
|
+
var model = editor$1.getModel();
|
|
5704
|
+
var errorDecorations = [];
|
|
5705
|
+
if (model) {
|
|
5706
|
+
var updateDecorations = function updateDecorations() {
|
|
5707
|
+
var exprValue = model.getValue();
|
|
5708
|
+
var markers = validateExpr(exprValue);
|
|
5709
|
+
var newDecorations = markers.map(function (m) {
|
|
5710
|
+
return {
|
|
5711
|
+
range: new Range(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn),
|
|
5712
|
+
options: {
|
|
5713
|
+
className: 'expr-error-squiggly',
|
|
5714
|
+
hoverMessage: {
|
|
5715
|
+
value: m.message
|
|
5716
|
+
},
|
|
5717
|
+
minimap: {
|
|
5718
|
+
color: '#e51400',
|
|
5719
|
+
position: 1
|
|
5720
|
+
},
|
|
5721
|
+
overviewRuler: {
|
|
5722
|
+
color: '#e51400',
|
|
5723
|
+
position: editor.OverviewRulerLane.Right
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
};
|
|
5727
|
+
});
|
|
5728
|
+
errorDecorations = model.deltaDecorations(errorDecorations, newDecorations);
|
|
5729
|
+
};
|
|
5730
|
+
var validateDisposable = model.onDidChangeContent(updateDecorations);
|
|
5731
|
+
disposablesRef.current.push(validateDisposable);
|
|
5732
|
+
// Run initial validation
|
|
5733
|
+
updateDecorations();
|
|
5734
|
+
}
|
|
5735
|
+
// Inject CSS for the red squiggly underline
|
|
5736
|
+
var styleEl = document.createElement('style');
|
|
5737
|
+
styleEl.textContent = "\n .expr-error-squiggly {\n background: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 3' preserveAspectRatio='none'%3E%3Cpath d='M0,2.5 L1.5,1 L3,2.5 L4.5,1 L6,2.5' stroke='%23e51400' stroke-width='0.6' fill='none'/%3E%3C/svg%3E\") repeat-x left bottom;\n background-size: 6px 3px;\n padding-bottom: 3px;\n }\n ";
|
|
5738
|
+
document.head.appendChild(styleEl);
|
|
5739
|
+
disposablesRef.current.push({
|
|
5740
|
+
dispose: function dispose() {
|
|
5741
|
+
return styleEl.remove();
|
|
5742
|
+
}
|
|
5743
|
+
});
|
|
5744
|
+
editorDidMount == null || editorDidMount(editor$1);
|
|
5745
|
+
};
|
|
5746
|
+
var handleChange = function handleChange(newValue, _e) {
|
|
5747
|
+
onChange == null || onChange(newValue);
|
|
5748
|
+
};
|
|
5749
|
+
var themeValue = themeMap$3[theme];
|
|
5750
|
+
return React.createElement("div", {
|
|
5751
|
+
className: 'ant-input' + (size ? " " + SIZE_MAP$3[size].className : '') + (disabled ? " ant-input-disabled " + containerDisabledClassName$3 : '') + (readOnly ? " " + containerReadOnlyClassName$3 : '') + (className ? " " + className : ''),
|
|
5752
|
+
style: {
|
|
5753
|
+
display: 'block',
|
|
5754
|
+
resize: 'vertical',
|
|
5755
|
+
overflow: 'auto',
|
|
5756
|
+
minHeight: SIZE_MAP$3[size].minHeight,
|
|
5757
|
+
maxHeight: maxHeight
|
|
5758
|
+
}
|
|
5759
|
+
}, React.createElement("div", {
|
|
5760
|
+
ref: containerRef,
|
|
5761
|
+
style: {
|
|
5762
|
+
height: '100%'
|
|
5763
|
+
}
|
|
5764
|
+
}, React.createElement(MonacoEditor, {
|
|
5765
|
+
language: EXPR_LANG_ID$1,
|
|
5766
|
+
theme: themeValue,
|
|
5767
|
+
value: value,
|
|
5768
|
+
onChange: handleChange,
|
|
5769
|
+
options: {
|
|
5770
|
+
placeholder: placeholder,
|
|
5771
|
+
selectOnLineNumbers: true,
|
|
5772
|
+
fontSize: fontSize || 12,
|
|
5773
|
+
roundedSelection: false,
|
|
5774
|
+
scrollBeyondLastLine: false,
|
|
5775
|
+
readOnly: readOnly || disabled,
|
|
5776
|
+
minimap: {
|
|
5777
|
+
enabled: false
|
|
5778
|
+
},
|
|
5779
|
+
lineNumbers: 'off',
|
|
5780
|
+
lineNumbersMinChars: 0,
|
|
5781
|
+
glyphMargin: false,
|
|
5782
|
+
folding: false,
|
|
5783
|
+
lineDecorationsWidth: 0,
|
|
5784
|
+
overviewRulerLanes: 0,
|
|
5785
|
+
overviewRulerBorder: false,
|
|
5786
|
+
hideCursorInOverviewRuler: true,
|
|
5787
|
+
hover: {
|
|
5788
|
+
enabled: true,
|
|
5789
|
+
delay: 200
|
|
5790
|
+
},
|
|
5791
|
+
fixedOverflowWidgets: true,
|
|
5792
|
+
renderLineHighlight: 'none',
|
|
5793
|
+
renderValidationDecorations: 'on',
|
|
5794
|
+
scrollbar: {
|
|
5795
|
+
vertical: 'hidden',
|
|
5796
|
+
horizontal: 'auto'
|
|
5797
|
+
},
|
|
5798
|
+
automaticLayout: true,
|
|
5799
|
+
wordWrap: 'on'
|
|
5800
|
+
},
|
|
5801
|
+
editorDidMount: handleEditorMount
|
|
5802
|
+
})));
|
|
5803
|
+
}
|
|
5804
|
+
|
|
5805
|
+
export { ExprEditor as ExprMonacoEditor, PromQLEditor as PromQLMonacoEditor, SqlEditor as SqlMonacoEditor, YamlEditor as YamlMonacoEditor };
|
|
3824
5806
|
//# sourceMappingURL=monaco-editor.esm.js.map
|