@duckcodeailabs/dql-core 1.6.18 → 1.6.19
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/apps/app-document.d.ts +2 -1
- package/dist/apps/app-document.d.ts.map +1 -1
- package/dist/apps/app-document.js +18 -5
- package/dist/apps/app-document.js.map +1 -1
- package/dist/apps/dashboard-document.d.ts +19 -0
- package/dist/apps/dashboard-document.d.ts.map +1 -1
- package/dist/apps/dashboard-document.js +72 -0
- package/dist/apps/dashboard-document.js.map +1 -1
- package/dist/apps/index.d.ts +1 -1
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/ast/nodes.d.ts +40 -1
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/ast/nodes.js +1 -0
- package/dist/ast/nodes.js.map +1 -1
- package/dist/ast/printer.d.ts.map +1 -1
- package/dist/ast/printer.js +40 -0
- package/dist/ast/printer.js.map +1 -1
- package/dist/formatter/formatter.d.ts.map +1 -1
- package/dist/formatter/formatter.js +66 -0
- package/dist/formatter/formatter.js.map +1 -1
- package/dist/lineage/builder.d.ts +35 -0
- package/dist/lineage/builder.d.ts.map +1 -1
- package/dist/lineage/builder.js +96 -8
- package/dist/lineage/builder.js.map +1 -1
- package/dist/lineage/index.d.ts +1 -1
- package/dist/lineage/index.d.ts.map +1 -1
- package/dist/lineage/index.js.map +1 -1
- package/dist/lineage/query.d.ts +30 -1
- package/dist/lineage/query.d.ts.map +1 -1
- package/dist/lineage/query.js +248 -1
- package/dist/lineage/query.js.map +1 -1
- package/dist/manifest/builder.d.ts +6 -1
- package/dist/manifest/builder.d.ts.map +1 -1
- package/dist/manifest/builder.js +359 -13
- package/dist/manifest/builder.js.map +1 -1
- package/dist/manifest/types.d.ts +52 -0
- package/dist/manifest/types.d.ts.map +1 -1
- package/dist/parser/parser.d.ts +4 -0
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +262 -20
- package/dist/parser/parser.js.map +1 -1
- package/dist/semantic/analyzer.d.ts.map +1 -1
- package/dist/semantic/analyzer.js +4 -0
- package/dist/semantic/analyzer.js.map +1 -1
- package/dist/semantic/providers/dbt-provider.js +130 -4
- package/dist/semantic/providers/dbt-provider.js.map +1 -1
- package/dist/semantic/providers/registry.js +68 -4
- package/dist/semantic/providers/registry.js.map +1 -1
- package/dist/semantic/providers/snowflake-provider.d.ts.map +1 -1
- package/dist/semantic/providers/snowflake-provider.js +60 -8
- package/dist/semantic/providers/snowflake-provider.js.map +1 -1
- package/dist/semantic/semantic-layer.d.ts.map +1 -1
- package/dist/semantic/semantic-layer.js +11 -20
- package/dist/semantic/semantic-layer.js.map +1 -1
- package/package.json +1 -1
package/dist/parser/parser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DiagnosticReporter, DQLSyntaxError } from '../errors/reporter.js';
|
|
2
2
|
import { Lexer } from '../lexer/lexer.js';
|
|
3
|
-
import { TokenType, CHART_TYPES, FILTER_TYPES, normalizeChartType } from '../lexer/token.js';
|
|
3
|
+
import { TokenType, CHART_TYPES, FILTER_TYPES, SQL_START_KEYWORDS, normalizeChartType } from '../lexer/token.js';
|
|
4
4
|
import { NodeKind, } from '../ast/nodes.js';
|
|
5
5
|
const DRAFT_STRING_METADATA_FIELDS = new Set([
|
|
6
6
|
'first_asked',
|
|
@@ -81,6 +81,9 @@ export class Parser {
|
|
|
81
81
|
if (this.check(TokenType.BlockKeyword)) {
|
|
82
82
|
return this.parseBlockDecl(decorators);
|
|
83
83
|
}
|
|
84
|
+
if (this.check(TokenType.DomainKeyword)) {
|
|
85
|
+
return this.parseDomainDecl(decorators);
|
|
86
|
+
}
|
|
84
87
|
if (this.check(TokenType.TermKeyword)) {
|
|
85
88
|
return this.parseTermDecl(decorators);
|
|
86
89
|
}
|
|
@@ -96,7 +99,7 @@ export class Parser {
|
|
|
96
99
|
if (this.check(TokenType.EOF)) {
|
|
97
100
|
return null;
|
|
98
101
|
}
|
|
99
|
-
this.error(`Unexpected token '${this.current().value}'. Expected 'dashboard', 'digest', 'workbook', 'chart', 'block', 'term', 'business_view', or 'import'.`);
|
|
102
|
+
this.error(`Unexpected token '${this.current().value}'. Expected 'dashboard', 'digest', 'workbook', 'chart', 'domain', 'block', 'term', 'business_view', or 'import'.`);
|
|
100
103
|
return null;
|
|
101
104
|
}
|
|
102
105
|
parseFilterCall() {
|
|
@@ -631,6 +634,90 @@ export class Parser {
|
|
|
631
634
|
makeSpan(start, end) {
|
|
632
635
|
return { start: start.start, end: end.end };
|
|
633
636
|
}
|
|
637
|
+
// ---- Domain Declaration ----
|
|
638
|
+
parseDomainDecl(decorators) {
|
|
639
|
+
const start = decorators.length > 0 ? decorators[0].span : this.currentSpan();
|
|
640
|
+
this.expect(TokenType.DomainKeyword);
|
|
641
|
+
const nameToken = this.expect(TokenType.StringLiteral);
|
|
642
|
+
this.expect(TokenType.LeftBrace);
|
|
643
|
+
let owner;
|
|
644
|
+
let businessOwner;
|
|
645
|
+
let boundedContext;
|
|
646
|
+
let sourceSystems;
|
|
647
|
+
let primaryTerms;
|
|
648
|
+
let reviewCadence;
|
|
649
|
+
let tags;
|
|
650
|
+
let businessOutcome;
|
|
651
|
+
let description;
|
|
652
|
+
while (!this.check(TokenType.RightBrace) && !this.isAtEnd()) {
|
|
653
|
+
if (this.check(TokenType.OwnerKeyword)) {
|
|
654
|
+
this.advance();
|
|
655
|
+
this.expect(TokenType.Equals);
|
|
656
|
+
owner = this.expect(TokenType.StringLiteral).value;
|
|
657
|
+
}
|
|
658
|
+
else if (this.check(TokenType.DescriptionKeyword)) {
|
|
659
|
+
this.advance();
|
|
660
|
+
this.expect(TokenType.Equals);
|
|
661
|
+
description = this.expectStringLike().value;
|
|
662
|
+
}
|
|
663
|
+
else if (this.check(TokenType.TagsKeyword)) {
|
|
664
|
+
this.advance();
|
|
665
|
+
this.expect(TokenType.Equals);
|
|
666
|
+
tags = this.parseStringArrayValues();
|
|
667
|
+
}
|
|
668
|
+
else if (this.check(TokenType.Identifier)
|
|
669
|
+
&& (this.current().value === 'businessOwner'
|
|
670
|
+
|| this.current().value === 'boundedContext'
|
|
671
|
+
|| this.current().value === 'sourceSystems'
|
|
672
|
+
|| this.current().value === 'primaryTerms'
|
|
673
|
+
|| this.current().value === 'reviewCadence'
|
|
674
|
+
|| this.current().value === 'businessOutcome')) {
|
|
675
|
+
const keyToken = this.advance();
|
|
676
|
+
this.expect(TokenType.Equals);
|
|
677
|
+
if (keyToken.value === 'businessOwner') {
|
|
678
|
+
businessOwner = this.expect(TokenType.StringLiteral).value;
|
|
679
|
+
}
|
|
680
|
+
else if (keyToken.value === 'boundedContext') {
|
|
681
|
+
boundedContext = this.expectStringLike().value;
|
|
682
|
+
}
|
|
683
|
+
else if (keyToken.value === 'sourceSystems') {
|
|
684
|
+
sourceSystems = this.parseStringArrayValues();
|
|
685
|
+
}
|
|
686
|
+
else if (keyToken.value === 'primaryTerms') {
|
|
687
|
+
primaryTerms = this.parseStringArrayValues();
|
|
688
|
+
}
|
|
689
|
+
else if (keyToken.value === 'reviewCadence') {
|
|
690
|
+
reviewCadence = this.expect(TokenType.StringLiteral).value;
|
|
691
|
+
}
|
|
692
|
+
else if (keyToken.value === 'businessOutcome') {
|
|
693
|
+
businessOutcome = this.expect(TokenType.StringLiteral).value;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
else if (this.check(TokenType.RightBrace)) {
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
this.error(`Unexpected token '${this.current().value}' inside domain. Expected 'owner', 'businessOwner', 'boundedContext', 'sourceSystems', 'primaryTerms', 'reviewCadence', 'tags', 'businessOutcome', 'description', or '}'.`);
|
|
701
|
+
this.advance();
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
this.expect(TokenType.RightBrace);
|
|
705
|
+
return {
|
|
706
|
+
kind: NodeKind.DomainDecl,
|
|
707
|
+
name: nameToken.value,
|
|
708
|
+
owner,
|
|
709
|
+
businessOwner,
|
|
710
|
+
boundedContext,
|
|
711
|
+
sourceSystems,
|
|
712
|
+
primaryTerms,
|
|
713
|
+
reviewCadence,
|
|
714
|
+
tags,
|
|
715
|
+
businessOutcome,
|
|
716
|
+
description,
|
|
717
|
+
decorators,
|
|
718
|
+
span: this.makeSpan(start, this.previousSpan()),
|
|
719
|
+
};
|
|
720
|
+
}
|
|
634
721
|
// ---- Business Term Declaration ----
|
|
635
722
|
parseTermDecl(decorators) {
|
|
636
723
|
const start = decorators.length > 0 ? decorators[0].span : this.currentSpan();
|
|
@@ -928,11 +1015,20 @@ export class Parser {
|
|
|
928
1015
|
let blockType;
|
|
929
1016
|
let metricRef;
|
|
930
1017
|
let metricsRef;
|
|
931
|
-
let
|
|
1018
|
+
let parsedDimensions;
|
|
932
1019
|
let description;
|
|
933
1020
|
let tags;
|
|
934
1021
|
let owner;
|
|
935
1022
|
let termRefs;
|
|
1023
|
+
let pattern;
|
|
1024
|
+
let grain;
|
|
1025
|
+
let entities;
|
|
1026
|
+
let outputs;
|
|
1027
|
+
let allowedFilters;
|
|
1028
|
+
let parameterPolicy;
|
|
1029
|
+
let filterBindings;
|
|
1030
|
+
let sourceSystems;
|
|
1031
|
+
let replacementFor;
|
|
936
1032
|
let params;
|
|
937
1033
|
let query;
|
|
938
1034
|
let visualization;
|
|
@@ -1015,7 +1111,7 @@ export class Parser {
|
|
|
1015
1111
|
this.expect(TokenType.Equals);
|
|
1016
1112
|
const arrExpr = this.parseArrayLiteral();
|
|
1017
1113
|
if (arrExpr.kind === NodeKind.ArrayLiteral) {
|
|
1018
|
-
|
|
1114
|
+
parsedDimensions = arrExpr.elements
|
|
1019
1115
|
.filter((e) => e.kind === NodeKind.StringLiteral)
|
|
1020
1116
|
.map((e) => e.value);
|
|
1021
1117
|
}
|
|
@@ -1047,25 +1143,72 @@ export class Parser {
|
|
|
1047
1143
|
this.expect(TokenType.Equals);
|
|
1048
1144
|
termRefs = this.parseStringArrayValues();
|
|
1049
1145
|
}
|
|
1146
|
+
else if (this.check(TokenType.Identifier)
|
|
1147
|
+
&& (this.current().value === 'grain'
|
|
1148
|
+
|| this.current().value === 'pattern'
|
|
1149
|
+
|| this.current().value === 'entities'
|
|
1150
|
+
|| this.current().value === 'outputs'
|
|
1151
|
+
|| this.current().value === 'allowedFilters'
|
|
1152
|
+
|| this.current().value === 'sourceSystems'
|
|
1153
|
+
|| this.current().value === 'replacementFor')) {
|
|
1154
|
+
const keyToken = this.advance();
|
|
1155
|
+
this.expect(TokenType.Equals);
|
|
1156
|
+
if (keyToken.value === 'pattern') {
|
|
1157
|
+
const val = this.expect(TokenType.StringLiteral);
|
|
1158
|
+
pattern = val.value;
|
|
1159
|
+
}
|
|
1160
|
+
else if (keyToken.value === 'grain') {
|
|
1161
|
+
const val = this.expect(TokenType.StringLiteral);
|
|
1162
|
+
grain = val.value;
|
|
1163
|
+
}
|
|
1164
|
+
else if (keyToken.value === 'entities') {
|
|
1165
|
+
entities = this.parseStringArrayValues();
|
|
1166
|
+
}
|
|
1167
|
+
else if (keyToken.value === 'outputs') {
|
|
1168
|
+
outputs = this.parseStringArrayValues();
|
|
1169
|
+
}
|
|
1170
|
+
else if (keyToken.value === 'allowedFilters') {
|
|
1171
|
+
allowedFilters = this.parseStringArrayValues();
|
|
1172
|
+
}
|
|
1173
|
+
else if (keyToken.value === 'sourceSystems') {
|
|
1174
|
+
sourceSystems = this.parseStringArrayValues();
|
|
1175
|
+
}
|
|
1176
|
+
else if (keyToken.value === 'replacementFor') {
|
|
1177
|
+
replacementFor = this.parseStringArrayValues();
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1050
1180
|
else if (this.check(TokenType.ParamsKeyword)) {
|
|
1051
1181
|
params = this.parseBlockParams();
|
|
1052
1182
|
}
|
|
1183
|
+
else if (this.check(TokenType.Identifier)
|
|
1184
|
+
&& this.current().value === 'parameterPolicy') {
|
|
1185
|
+
parameterPolicy = this.parseBlockParameterPolicy();
|
|
1186
|
+
}
|
|
1187
|
+
else if (this.check(TokenType.Identifier)
|
|
1188
|
+
&& this.current().value === 'filterBindings') {
|
|
1189
|
+
filterBindings = this.parseBlockFilterBindings();
|
|
1190
|
+
}
|
|
1053
1191
|
else if (this.check(TokenType.QueryKeyword)) {
|
|
1054
1192
|
this.advance();
|
|
1055
|
-
this.
|
|
1056
|
-
|
|
1057
|
-
const sqlToken = this.advance();
|
|
1058
|
-
const interpolations = this.extractInterpolations(sqlToken.value, sqlToken.span);
|
|
1059
|
-
query = {
|
|
1060
|
-
kind: NodeKind.SQLQuery,
|
|
1061
|
-
rawSQL: sqlToken.value,
|
|
1062
|
-
interpolations,
|
|
1063
|
-
span: sqlToken.span,
|
|
1064
|
-
};
|
|
1193
|
+
if (this.check(TokenType.LeftBrace)) {
|
|
1194
|
+
query = this.parseLegacyBlockQuerySection();
|
|
1065
1195
|
}
|
|
1066
1196
|
else {
|
|
1067
|
-
|
|
1068
|
-
|
|
1197
|
+
this.expect(TokenType.Equals);
|
|
1198
|
+
if (this.check(TokenType.TripleQuoteString)) {
|
|
1199
|
+
const sqlToken = this.advance();
|
|
1200
|
+
const interpolations = this.extractInterpolations(sqlToken.value, sqlToken.span);
|
|
1201
|
+
query = {
|
|
1202
|
+
kind: NodeKind.SQLQuery,
|
|
1203
|
+
rawSQL: sqlToken.value,
|
|
1204
|
+
interpolations,
|
|
1205
|
+
span: sqlToken.span,
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
else {
|
|
1209
|
+
// Fallback: parse as inline SQL
|
|
1210
|
+
query = this.parseSQLQuery();
|
|
1211
|
+
}
|
|
1069
1212
|
}
|
|
1070
1213
|
}
|
|
1071
1214
|
else if (this.check(TokenType.VisualizationKeyword)) {
|
|
@@ -1264,13 +1407,13 @@ export class Parser {
|
|
|
1264
1407
|
this.skipBalancedBlock();
|
|
1265
1408
|
continue;
|
|
1266
1409
|
}
|
|
1267
|
-
this.error(`Unexpected token '${this.current().value}' inside block. Expected 'domain', 'type', 'status', 'datalex_contract', 'metric', 'metrics', 'dimensions', 'description', 'tags', 'owner', 'terms', 'params', 'query', 'visualization', 'tests', 'llmContext', 'invariants', 'examples', 'businessOutcome', 'businessOwner', 'decisionUse', 'reviewCadence', 'businessRules', 'caveats', Tier-2 draft metadata fields, or '}'.`);
|
|
1410
|
+
this.error(`Unexpected token '${this.current().value}' inside block. Expected 'domain', 'type', 'status', 'datalex_contract', 'metric', 'metrics', 'dimensions', 'description', 'tags', 'owner', 'terms', 'pattern', 'grain', 'entities', 'outputs', 'allowedFilters', 'parameterPolicy', 'filterBindings', 'sourceSystems', 'replacementFor', 'params', 'query', 'visualization', 'tests', 'llmContext', 'invariants', 'examples', 'businessOutcome', 'businessOwner', 'decisionUse', 'reviewCadence', 'businessRules', 'caveats', Tier-2 draft metadata fields, or '}'.`);
|
|
1268
1411
|
this.advance();
|
|
1269
1412
|
}
|
|
1270
1413
|
}
|
|
1271
1414
|
if (!blockType) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1415
|
+
blockType = 'custom';
|
|
1416
|
+
this.reporter.warning(`Block "${nameToken.value}" is missing a type declaration. DQL treated it as type = "custom" for backward compatibility.`, nameToken.span);
|
|
1274
1417
|
}
|
|
1275
1418
|
this.expect(TokenType.RightBrace);
|
|
1276
1419
|
return {
|
|
@@ -1280,11 +1423,21 @@ export class Parser {
|
|
|
1280
1423
|
blockType,
|
|
1281
1424
|
metricRef,
|
|
1282
1425
|
metricsRef,
|
|
1283
|
-
dimensionsRef,
|
|
1426
|
+
dimensionsRef: blockType === 'semantic' ? parsedDimensions : undefined,
|
|
1284
1427
|
description,
|
|
1285
1428
|
tags,
|
|
1286
1429
|
owner,
|
|
1287
1430
|
termRefs,
|
|
1431
|
+
pattern,
|
|
1432
|
+
grain,
|
|
1433
|
+
entities,
|
|
1434
|
+
outputs,
|
|
1435
|
+
dimensions: blockType === 'semantic' ? undefined : parsedDimensions,
|
|
1436
|
+
allowedFilters,
|
|
1437
|
+
parameterPolicy,
|
|
1438
|
+
filterBindings,
|
|
1439
|
+
sourceSystems,
|
|
1440
|
+
replacementFor,
|
|
1288
1441
|
params,
|
|
1289
1442
|
query,
|
|
1290
1443
|
visualization,
|
|
@@ -1319,6 +1472,43 @@ export class Parser {
|
|
|
1319
1472
|
span: this.makeSpan(start, this.previousSpan()),
|
|
1320
1473
|
};
|
|
1321
1474
|
}
|
|
1475
|
+
parseLegacyBlockQuerySection() {
|
|
1476
|
+
const start = this.currentSpan();
|
|
1477
|
+
this.expect(TokenType.LeftBrace);
|
|
1478
|
+
let query;
|
|
1479
|
+
while (!this.check(TokenType.RightBrace) && !this.isAtEnd()) {
|
|
1480
|
+
if (this.check(TokenType.Identifier) && this.current().value === 'sql') {
|
|
1481
|
+
this.advance();
|
|
1482
|
+
this.expect(TokenType.Equals);
|
|
1483
|
+
if (this.check(TokenType.TripleQuoteString)) {
|
|
1484
|
+
const sqlToken = this.advance();
|
|
1485
|
+
const interpolations = this.extractInterpolations(sqlToken.value, sqlToken.span);
|
|
1486
|
+
query = {
|
|
1487
|
+
kind: NodeKind.SQLQuery,
|
|
1488
|
+
rawSQL: sqlToken.value,
|
|
1489
|
+
interpolations,
|
|
1490
|
+
span: sqlToken.span,
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
else {
|
|
1494
|
+
query = this.parseSQLQuery();
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
else if (this.check(TokenType.Identifier)
|
|
1498
|
+
&& SQL_START_KEYWORDS.has(this.current().value.toUpperCase())) {
|
|
1499
|
+
query = this.parseSQLQuery();
|
|
1500
|
+
}
|
|
1501
|
+
else {
|
|
1502
|
+
this.error(`Unexpected token '${this.current().value}' inside query section. Expected 'sql' or inline SQL.`);
|
|
1503
|
+
this.advance();
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
this.expect(TokenType.RightBrace);
|
|
1507
|
+
if (!query) {
|
|
1508
|
+
this.reporter.error('Query section must contain sql = """...""" or inline SQL.', this.makeSpan(start, this.previousSpan()));
|
|
1509
|
+
}
|
|
1510
|
+
return query;
|
|
1511
|
+
}
|
|
1322
1512
|
parseBlockParams() {
|
|
1323
1513
|
const start = this.currentSpan();
|
|
1324
1514
|
this.expect(TokenType.ParamsKeyword);
|
|
@@ -1350,6 +1540,58 @@ export class Parser {
|
|
|
1350
1540
|
span: this.makeSpan(start, this.previousSpan()),
|
|
1351
1541
|
};
|
|
1352
1542
|
}
|
|
1543
|
+
parseBlockParameterPolicy() {
|
|
1544
|
+
const entries = [];
|
|
1545
|
+
this.expect(TokenType.Identifier);
|
|
1546
|
+
this.expect(TokenType.LeftBrace);
|
|
1547
|
+
while (!this.check(TokenType.RightBrace) && !this.isAtEnd()) {
|
|
1548
|
+
const entryStart = this.currentSpan();
|
|
1549
|
+
const nameToken = this.current();
|
|
1550
|
+
if (nameToken.type === TokenType.Identifier || this.isBlockFieldKeyword(nameToken.type)) {
|
|
1551
|
+
this.advance();
|
|
1552
|
+
}
|
|
1553
|
+
else {
|
|
1554
|
+
this.error(`Expected parameter name inside parameterPolicy, got '${nameToken.value}'.`);
|
|
1555
|
+
this.advance();
|
|
1556
|
+
continue;
|
|
1557
|
+
}
|
|
1558
|
+
this.expect(TokenType.Equals);
|
|
1559
|
+
const policy = this.expectStringLike();
|
|
1560
|
+
entries.push({
|
|
1561
|
+
name: nameToken.value,
|
|
1562
|
+
policy: policy.value,
|
|
1563
|
+
span: this.makeSpan(entryStart, this.previousSpan()),
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
this.expect(TokenType.RightBrace);
|
|
1567
|
+
return entries;
|
|
1568
|
+
}
|
|
1569
|
+
parseBlockFilterBindings() {
|
|
1570
|
+
const entries = [];
|
|
1571
|
+
this.expect(TokenType.Identifier);
|
|
1572
|
+
this.expect(TokenType.LeftBrace);
|
|
1573
|
+
while (!this.check(TokenType.RightBrace) && !this.isAtEnd()) {
|
|
1574
|
+
const entryStart = this.currentSpan();
|
|
1575
|
+
const nameToken = this.current();
|
|
1576
|
+
if (nameToken.type === TokenType.Identifier || this.isBlockFieldKeyword(nameToken.type)) {
|
|
1577
|
+
this.advance();
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
this.error(`Expected business filter name inside filterBindings, got '${nameToken.value}'.`);
|
|
1581
|
+
this.advance();
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1584
|
+
this.expect(TokenType.Equals);
|
|
1585
|
+
const binding = this.expectStringLike();
|
|
1586
|
+
entries.push({
|
|
1587
|
+
filter: nameToken.value,
|
|
1588
|
+
binding: binding.value,
|
|
1589
|
+
span: this.makeSpan(entryStart, this.previousSpan()),
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
this.expect(TokenType.RightBrace);
|
|
1593
|
+
return entries;
|
|
1594
|
+
}
|
|
1353
1595
|
parseBlockVisualization() {
|
|
1354
1596
|
const start = this.currentSpan();
|
|
1355
1597
|
this.expect(TokenType.VisualizationKeyword);
|