@sap/cds-compiler 3.4.2 → 3.5.0
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/CHANGELOG.md +80 -0
- package/README.md +1 -0
- package/bin/cds_update_identifiers.js +5 -5
- package/bin/cdsc.js +15 -16
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +2 -2
- package/doc/CHANGELOG_BETA.md +9 -1
- package/doc/CHANGELOG_DEPRECATED.md +2 -0
- package/lib/api/main.js +61 -59
- package/lib/api/options.js +4 -2
- package/lib/api/validate.js +2 -2
- package/lib/base/cleanSymbols.js +2 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +6 -6
- package/lib/base/location.js +11 -12
- package/lib/base/message-registry.js +177 -58
- package/lib/base/messages.js +252 -180
- package/lib/base/model.js +14 -11
- package/lib/base/node-helpers.js +9 -10
- package/lib/base/optionProcessorHelper.js +138 -129
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +5 -5
- package/lib/checks/annotationsOData.js +4 -4
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +3 -3
- package/lib/checks/defaultValues.js +3 -3
- package/lib/checks/elements.js +7 -7
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +4 -4
- package/lib/checks/managedInType.js +1 -1
- package/lib/checks/managedWithoutKeys.js +1 -1
- package/lib/checks/nonexpandableStructured.js +5 -3
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +5 -6
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -2
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +4 -4
- package/lib/checks/types.js +7 -7
- package/lib/checks/utils.js +4 -4
- package/lib/checks/validator.js +16 -13
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -7
- package/lib/compiler/builtins.js +14 -14
- package/lib/compiler/checks.js +123 -48
- package/lib/compiler/define.js +12 -13
- package/lib/compiler/extend.js +266 -60
- package/lib/compiler/finalize-parse-cdl.js +10 -5
- package/lib/compiler/index.js +17 -14
- package/lib/compiler/populate.js +14 -6
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +27 -16
- package/lib/compiler/tweak-assocs.js +5 -6
- package/lib/compiler/utils.js +20 -0
- package/lib/edm/annotations/genericTranslation.js +604 -358
- package/lib/edm/annotations/preprocessAnnotations.js +39 -35
- package/lib/edm/csn2edm.js +275 -222
- package/lib/edm/edm.js +17 -3
- package/lib/edm/edmAnnoPreprocessor.js +6 -6
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +107 -77
- package/lib/edm/edmUtils.js +44 -5
- package/lib/gen/Dictionary.json +210 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +67 -63
- package/lib/gen/language.tokens +81 -81
- package/lib/gen/languageLexer.interp +4 -10
- package/lib/gen/languageLexer.js +854 -869
- package/lib/gen/languageLexer.tokens +79 -81
- package/lib/gen/languageParser.js +14309 -13832
- package/lib/inspect/inspectModelStatistics.js +2 -2
- package/lib/inspect/inspectPropagation.js +6 -6
- package/lib/inspect/inspectUtils.js +2 -2
- package/lib/json/from-csn.js +102 -55
- package/lib/json/to-csn.js +119 -198
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +113 -133
- package/lib/language/language.g4 +1550 -1506
- package/lib/language/multiLineStringParser.js +3 -3
- package/lib/language/textUtils.js +2 -2
- package/lib/main.js +3 -3
- package/lib/model/csnRefs.js +5 -0
- package/lib/model/csnUtils.js +130 -122
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/model/sortViews.js +4 -6
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +100 -0
- package/lib/optionProcessor.js +5 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +12 -12
- package/lib/render/toCdl.js +311 -276
- package/lib/render/toHdbcds.js +97 -94
- package/lib/render/toRename.js +5 -5
- package/lib/render/toSql.js +127 -223
- package/lib/render/utils/common.js +141 -108
- package/lib/render/utils/delta.js +227 -0
- package/lib/render/utils/sql.js +22 -6
- package/lib/render/utils/stringEscapes.js +3 -3
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/assertUnique.js +13 -12
- package/lib/transform/db/associations.js +5 -5
- package/lib/transform/db/cdsPersistence.js +10 -8
- package/lib/transform/db/constraints.js +14 -14
- package/lib/transform/db/expansion.js +20 -22
- package/lib/transform/db/flattening.js +24 -42
- package/lib/transform/db/groupByOrderBy.js +3 -3
- package/lib/transform/db/temporal.js +6 -6
- package/lib/transform/db/transformExists.js +23 -23
- package/lib/transform/db/views.js +16 -16
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/draft/db.js +10 -10
- package/lib/transform/draft/odata.js +2 -2
- package/lib/transform/forOdataNew.js +8 -29
- package/lib/transform/forRelationalDB.js +16 -6
- package/lib/transform/localized.js +11 -10
- package/lib/transform/odata/toFinalBaseType.js +41 -27
- package/lib/transform/odata/typesExposure.js +113 -47
- package/lib/transform/parseExpr.js +209 -106
- package/lib/transform/transformUtilsNew.js +17 -10
- package/lib/transform/translateAssocsToJoins.js +24 -19
- package/lib/transform/universalCsn/coreComputed.js +10 -10
- package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
- package/lib/transform/universalCsn/utils.js +3 -3
- package/lib/utils/file.js +5 -5
- package/lib/utils/moduleResolve.js +13 -13
- package/lib/utils/objectUtils.js +6 -6
- package/lib/utils/term.js +5 -2
- package/lib/utils/timetrace.js +51 -24
- package/package.json +5 -8
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/message-explanations.json +1 -1
- package/share/messages/redirected-to-complex.md +4 -4
- package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
- package/lib/modelCompare/filter.js +0 -83
package/lib/language/language.g4
CHANGED
|
@@ -120,6 +120,8 @@ options {
|
|
|
120
120
|
superClass = genericAntlrParser;
|
|
121
121
|
}
|
|
122
122
|
tokens {
|
|
123
|
+
ELEMENT, // used with setLocalToken()
|
|
124
|
+
MASKED, // used with setLocalToken()
|
|
123
125
|
VIRTUAL, // used with setLocalToken()
|
|
124
126
|
OVER, // used with setLocalTokenIfBefore()
|
|
125
127
|
HelperToken1, // used with setLocalToken(), does not appear in messages
|
|
@@ -132,6 +134,20 @@ tokens {
|
|
|
132
134
|
COMPOSITIONofBRACE // via token rewrite in rule typeAssociationBase
|
|
133
135
|
}
|
|
134
136
|
|
|
137
|
+
// Content:
|
|
138
|
+
// - top-level: USING, NAMESPACE, artifactDefOrExtend (start rule: start)
|
|
139
|
+
// - main definitions and annotation def
|
|
140
|
+
// - member definitions
|
|
141
|
+
// - EXTEND and ANNOTATE
|
|
142
|
+
// - type expressions
|
|
143
|
+
// - queries: the main query hierarchy (start rule: queryEOF)
|
|
144
|
+
// - queries: columns and other clauses
|
|
145
|
+
// - conditions and expressions (start rule: conditionEOF)
|
|
146
|
+
// - paths and functions
|
|
147
|
+
// - annotation assignments
|
|
148
|
+
// - literal values and identifiers
|
|
149
|
+
// - Lexer: spaces, literal values, reserved keywords, unreserved keywords, identifier
|
|
150
|
+
|
|
135
151
|
// Top-Level -----------------------------------------------------------------
|
|
136
152
|
|
|
137
153
|
start returns [ source ] locals [ _sync = 'recover' ]
|
|
@@ -140,24 +156,14 @@ start returns [ source ] locals [ _sync = 'recover' ]
|
|
|
140
156
|
usingDeclaration[$source]*
|
|
141
157
|
(
|
|
142
158
|
namespaceDeclaration[$source]
|
|
143
|
-
( usingDeclaration[$source] |
|
|
159
|
+
( usingDeclaration[$source] | artifactDefOrExtend[$source] )*
|
|
144
160
|
|
|
|
145
|
-
|
|
146
|
-
( usingDeclaration[$source] |
|
|
161
|
+
artifactDefOrExtend[$source]
|
|
162
|
+
( usingDeclaration[$source] | artifactDefOrExtend[$source] )*
|
|
147
163
|
)?
|
|
148
164
|
EOF
|
|
149
165
|
;
|
|
150
166
|
|
|
151
|
-
queryEOF returns [ query ]
|
|
152
|
-
:
|
|
153
|
-
q=queryExpression { $query = $q.query; } EOF
|
|
154
|
-
;
|
|
155
|
-
|
|
156
|
-
conditionEOF returns [ cond ]
|
|
157
|
-
:
|
|
158
|
-
c=condition { $cond = $c.cond; } EOF
|
|
159
|
-
;
|
|
160
|
-
|
|
161
167
|
namespaceDeclaration[ source ] locals[ decl = {} ]
|
|
162
168
|
@after { $source.namespace = this.attachLocation($decl); }
|
|
163
169
|
:
|
|
@@ -173,13 +179,9 @@ usingDeclaration[ source ] locals[ decl = {} ]
|
|
|
173
179
|
FROM str=String
|
|
174
180
|
{ $source.dependencies.push( this.quotedLiteral( $str, 'string' ) ); }
|
|
175
181
|
|
|
|
176
|
-
|
|
177
|
-
{ this.addItem( $decl, $source, 'usings', 'using' );; $decl.extern = $path.extern; }
|
|
178
|
-
( AS name=ident['Using'] { $decl.name = $name.id; }
|
|
179
|
-
| { this.classifyImplicitName( 'Using' ); }
|
|
180
|
-
)
|
|
182
|
+
usingProxy[ $source, $decl ]
|
|
181
183
|
( FROM str=String
|
|
182
|
-
{ $source.dependencies.push( $decl.fileDep = this.quotedLiteral( $str, 'string' ) );
|
|
184
|
+
{ $source.dependencies.push( $decl.fileDep = this.quotedLiteral( $str, 'string' ) ); }
|
|
183
185
|
)?
|
|
184
186
|
|
|
|
185
187
|
{ this.addItem( $decl, $source, 'usings', 'using' ); }
|
|
@@ -187,9 +189,9 @@ usingDeclaration[ source ] locals[ decl = {} ]
|
|
|
187
189
|
// to have some check in the future whether the external artifacts are
|
|
188
190
|
// really in the FROM source...
|
|
189
191
|
'{' { $decl.usings = this.createArray(); }
|
|
190
|
-
|
|
192
|
+
usingProxy[ $decl, {} ]
|
|
191
193
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
192
|
-
|
|
194
|
+
usingProxy[ $decl, {} ] )*
|
|
193
195
|
'}' { this.finalizeDictOrArray( $decl.usings ); }
|
|
194
196
|
( FROM str=String
|
|
195
197
|
{ $source.dependencies.push( $decl.fileDep = this.quotedLiteral( $str, 'string' ) ); }
|
|
@@ -198,175 +200,27 @@ usingDeclaration[ source ] locals[ decl = {} ]
|
|
|
198
200
|
';'
|
|
199
201
|
;
|
|
200
202
|
|
|
201
|
-
|
|
202
|
-
@after { this.attachLocation($
|
|
203
|
+
usingProxy[ outer, proxy ]
|
|
204
|
+
@after { this.attachLocation($proxy); }
|
|
203
205
|
:
|
|
204
|
-
{ $
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
(
|
|
206
|
+
{ if (!$proxy.location) $proxy.location = this.startLocation();
|
|
207
|
+
$proxy.extern = {}; }
|
|
208
|
+
simplePath[ $proxy.extern, 'global' ]
|
|
209
|
+
{ this.addItem( $proxy, $outer, 'usings', 'using' ); }
|
|
210
|
+
( AS name=ident['Using'] { $proxy.name = $name.id; }
|
|
208
211
|
| { this.classifyImplicitName( 'Using' ); }
|
|
209
212
|
)
|
|
210
213
|
;
|
|
211
214
|
|
|
212
|
-
|
|
213
|
-
:
|
|
214
|
-
simplePath[ $extern, 'global' ]
|
|
215
|
-
;
|
|
216
|
-
|
|
217
|
-
// We have two versions of the annotation assignment rule, because we do not
|
|
218
|
-
// want to let the ambiguity in select items (solution: "either" possibility)
|
|
219
|
-
// creep into all annotation assignments:
|
|
220
|
-
// view V(p) as select from E { // either: anno value "ref p", select item -x
|
|
221
|
-
// @anno :p - x as x; // or: anno value true, select item :p-x
|
|
222
|
-
// }
|
|
223
|
-
|
|
224
|
-
annotationAssignment_1[ art ] locals[ assignment = { name: {} } ]
|
|
225
|
-
@after { this.assignAnnotation( $art, $assignment ); }
|
|
226
|
-
:
|
|
227
|
-
annotationPath[ $assignment.name, 'anno' ]
|
|
228
|
-
annotationPathVariant[ $assignment.name ]?
|
|
229
|
-
(
|
|
230
|
-
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
231
|
-
val=annoValue[ $assignment ]
|
|
232
|
-
)?
|
|
233
|
-
;
|
|
234
|
-
|
|
235
|
-
annotationAssignment_paren[ art ]
|
|
236
|
-
:
|
|
237
|
-
'('
|
|
238
|
-
// allow completely useless `@()` with a warning, do not offer it for completion
|
|
239
|
-
{
|
|
240
|
-
this.meltKeywordToIdentifier();
|
|
241
|
-
if (this.isStraightBefore(')')) {
|
|
242
|
-
this.warning( 'syntax-anno-useless',
|
|
243
|
-
this.tokenLocation( this._input.LT(-2), this.getCurrentToken() ),
|
|
244
|
-
{ code: '@()' },
|
|
245
|
-
'Ignored useless $(CODE)' );
|
|
246
|
-
this.matchWildcard(); // we know it is the ')' - we do not reach the final match
|
|
247
|
-
return $ctx;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
annotationAssignment_1[ $art ]
|
|
251
|
-
( ','
|
|
252
|
-
{
|
|
253
|
-
this.meltKeywordToIdentifier();
|
|
254
|
-
if (this.isStraightBefore(')')) break; // allow ',' before ')'
|
|
255
|
-
}
|
|
256
|
-
annotationAssignment_1[ $art ]
|
|
257
|
-
)*
|
|
258
|
-
')'
|
|
259
|
-
;
|
|
260
|
-
|
|
261
|
-
annotationAssignment_fix[ art ] locals[ assignment ]
|
|
262
|
-
// value outside @(...)
|
|
263
|
-
@after {
|
|
264
|
-
if ($assignment) {
|
|
265
|
-
this.assignAnnotation( $art, $assignment );
|
|
266
|
-
this.docComment( $art );
|
|
267
|
-
}
|
|
268
|
-
} :
|
|
269
|
-
'@'
|
|
270
|
-
(
|
|
271
|
-
annotationAssignment_paren[ $art ]
|
|
272
|
-
|
|
|
273
|
-
{ $assignment = { name: {} }; }
|
|
274
|
-
annotationPath[ $assignment.name, 'anno' ]
|
|
275
|
-
annotationPathVariant[ $assignment.name ]?
|
|
276
|
-
{
|
|
277
|
-
var t = this.getCurrentToken();
|
|
278
|
-
if (t.text === ':')
|
|
279
|
-
this.warning( 'syntax-anno-short', $assignment.name.location,
|
|
280
|
-
{ code: '@(...)' },
|
|
281
|
-
'Better use $(CODE) for annotation assignments here' );
|
|
282
|
-
}
|
|
283
|
-
)
|
|
284
|
-
;
|
|
285
|
-
|
|
286
|
-
annotationAssignment_ll1[ art ] locals[ assignment ]
|
|
287
|
-
@after {
|
|
288
|
-
if ($assignment) {
|
|
289
|
-
this.assignAnnotation( $art, $assignment );
|
|
290
|
-
this.docComment( $art );
|
|
291
|
-
}
|
|
292
|
-
} :
|
|
293
|
-
'@'
|
|
294
|
-
(
|
|
295
|
-
annotationAssignment_paren[ $art ]
|
|
296
|
-
|
|
|
297
|
-
{ $assignment = { name: {} }; }
|
|
298
|
-
annotationPath[ $assignment.name, 'anno' ]
|
|
299
|
-
annotationPathVariant[ $assignment.name ]?
|
|
300
|
-
(
|
|
301
|
-
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
302
|
-
val=annoValue[ $assignment ]
|
|
303
|
-
)?
|
|
304
|
-
)
|
|
305
|
-
;
|
|
306
|
-
|
|
307
|
-
// Has previously used ATN, now via local token rewrite
|
|
308
|
-
annotationAssignment_atn[ art ] locals[ assignment ]
|
|
309
|
-
@after {
|
|
310
|
-
if ($assignment) {
|
|
311
|
-
this.assignAnnotation( $art, $assignment );
|
|
312
|
-
this.docComment( $art );
|
|
313
|
-
}
|
|
314
|
-
} :
|
|
315
|
-
'@'
|
|
316
|
-
(
|
|
317
|
-
annotationAssignment_paren[ $art ]
|
|
318
|
-
|
|
|
319
|
-
{ $assignment = { name: {} }; }
|
|
320
|
-
annotationPath[ $assignment.name, 'anno' ]
|
|
321
|
-
// '#' is in the follow set of this rule, as it is used in rule "selectItemDef"
|
|
322
|
-
// before an "expression" which can start with a '#' for an enum value
|
|
323
|
-
// -> used to introduce variant name if and only if in same line as previous token
|
|
324
|
-
{ this.setLocalToken( '#', 'HelperToken1', null, true ); }
|
|
325
|
-
(
|
|
326
|
-
HelperToken1 { this.meltKeywordToIdentifier(); }
|
|
327
|
-
variant=ident['variant'] { $assignment.name.variant = $variant.id; }
|
|
328
|
-
)?
|
|
329
|
-
// ':' is in the follow set of this rule, as it is used in rule "selectItemDef"
|
|
330
|
-
// before an "expression" which can start with a ':' for a parameter reference
|
|
331
|
-
// -> used to introduce assignment value if and only if in same line as previous token
|
|
332
|
-
{ this.setLocalToken( ':', 'HelperToken2', null, true ); }
|
|
333
|
-
( HelperToken2 // ':'
|
|
334
|
-
{ this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
335
|
-
(
|
|
336
|
-
val=annoValueBase[ $assignment ]
|
|
337
|
-
|
|
|
338
|
-
at='@'? annotationPath[ $assignment, 'ref', $at ]
|
|
339
|
-
{ this.setLocalToken( '#', 'HelperToken1', null, true ); } // see above
|
|
340
|
-
(
|
|
341
|
-
HelperToken1 { this.meltKeywordToIdentifier(); }
|
|
342
|
-
variant=ident['variant'] { $assignment.variant = $variant.id; }
|
|
343
|
-
)?
|
|
344
|
-
)
|
|
345
|
-
)?
|
|
346
|
-
)
|
|
347
|
-
;
|
|
348
|
-
|
|
349
|
-
// Main artifact definitions -------------------------------------------------
|
|
350
|
-
|
|
351
|
-
requiredSemi
|
|
352
|
-
: ';'
|
|
353
|
-
| { return $ctx; } // do not actually parse the closing brace
|
|
354
|
-
'}'
|
|
355
|
-
;
|
|
356
|
-
|
|
357
|
-
optionalSemi
|
|
358
|
-
: { this.noAssignmentInSameLine(); } // issue warning for } @Anno \n? NextDef
|
|
359
|
-
';'?
|
|
360
|
-
;
|
|
361
|
-
|
|
362
|
-
artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent` as parameter name!
|
|
215
|
+
artifactDefOrExtend[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent` as parameter name!
|
|
363
216
|
@after{ /* #ATN 1 */ }
|
|
364
217
|
:
|
|
365
218
|
{ $art.location = this.startLocation(); this.docComment( $art ); }
|
|
366
219
|
annotationAssignment_ll1[ $art ]*
|
|
367
220
|
(
|
|
368
221
|
DEFINE?
|
|
369
|
-
(
|
|
222
|
+
( serviceDef[ $art, $outer, defOnly ]
|
|
223
|
+
| contextDef[ $art, $outer, defOnly ]
|
|
370
224
|
| entityDef[ $art, $outer ]
|
|
371
225
|
| typeDef[ $art, $outer ]
|
|
372
226
|
| aspectDef[ $art, $outer ]
|
|
@@ -377,26 +231,27 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
|
|
|
377
231
|
)
|
|
378
232
|
|
|
|
379
233
|
extend=EXTEND
|
|
380
|
-
{ if (defOnly)
|
|
234
|
+
{ if (defOnly) // TODO: nicer message text
|
|
381
235
|
this.error( 'syntax-extend-context', $extend,
|
|
382
|
-
{ code: 'EXTEND artifact',
|
|
383
|
-
'No $(CODE) within $(
|
|
236
|
+
{ code: 'EXTEND artifact', meta: defOnly },
|
|
237
|
+
'No $(CODE) within $(META) extensions' );
|
|
384
238
|
if (!$outer.extensions) $outer.extensions = [];
|
|
385
239
|
}
|
|
386
|
-
// #ATN: EXTEND
|
|
387
|
-
(
|
|
388
|
-
|
|
|
240
|
+
// #ATN: EXTEND art, while CONTEXT, ENTITY etc are not reserved
|
|
241
|
+
( extendService[ $art, $outer ]
|
|
242
|
+
| extendContext[ $art, $outer ]
|
|
243
|
+
| extendEntityOrAspect[ $art, $outer ] // or aspect
|
|
389
244
|
| extendProjection[ $art, $outer ]
|
|
390
245
|
| extendType[ $art, $outer ]
|
|
391
|
-
// Streamlined Syntax
|
|
246
|
+
// Streamlined Syntax; we won't add more kinds of the non-streamlined variants:
|
|
392
247
|
| extendArtifact[ $art, $outer ]
|
|
393
248
|
)
|
|
394
249
|
|
|
|
395
250
|
annotate=ANNOTATE
|
|
396
|
-
{ if (defOnly)
|
|
251
|
+
{ if (defOnly) // TODO: improve message text
|
|
397
252
|
this.error( 'syntax-extend-context', $annotate,
|
|
398
|
-
{ code: 'ANNOTATE artifact',
|
|
399
|
-
'No $(CODE) within $(
|
|
253
|
+
{ code: 'ANNOTATE artifact', meta: defOnly },
|
|
254
|
+
'No $(CODE) within $(META) extensions' );
|
|
400
255
|
if (!$outer.extensions) $outer.extensions = [];
|
|
401
256
|
this.meltKeywordToIdentifier();
|
|
402
257
|
}
|
|
@@ -404,16 +259,14 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
|
|
|
404
259
|
)
|
|
405
260
|
;
|
|
406
261
|
|
|
407
|
-
|
|
262
|
+
optArtifactsBlock[ art, defOnly = false ]
|
|
408
263
|
@after { this.attachLocation( $art ); }
|
|
409
264
|
:
|
|
410
|
-
(
|
|
411
|
-
{ this.addDef( $art, $outer, 'artifacts', $service ? 'service' : 'context', $name );
|
|
412
|
-
this.docComment( $art ); }
|
|
265
|
+
{ this.docComment( $art ); }
|
|
413
266
|
annotationAssignment_fix[ $art ]*
|
|
414
267
|
(
|
|
415
268
|
'{' { $art.artifacts = this.createDict(); $art.extensions = []; }
|
|
416
|
-
|
|
269
|
+
artifactDefOrExtend[ $art, defOnly ]*
|
|
417
270
|
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
418
271
|
optionalSemi
|
|
419
272
|
|
|
|
@@ -421,25 +274,110 @@ contextDef[ art, outer, defOnly = false ] locals[ name = {} ]
|
|
|
421
274
|
)
|
|
422
275
|
;
|
|
423
276
|
|
|
424
|
-
|
|
277
|
+
requiredSemi
|
|
278
|
+
: ';'
|
|
279
|
+
| { return $ctx; } // do not actually parse the closing brace
|
|
280
|
+
'}'
|
|
281
|
+
;
|
|
282
|
+
|
|
283
|
+
optionalSemi
|
|
284
|
+
: { this.noAssignmentInSameLine(); } // issue warning for } @Anno \n? NextDef
|
|
285
|
+
';'?
|
|
286
|
+
;
|
|
287
|
+
|
|
288
|
+
// Annotation def and main definitions ------------------------------------------
|
|
289
|
+
|
|
290
|
+
annotationDef[ art, outer ] locals[ name = {} ]
|
|
425
291
|
@after { this.attachLocation( $art ); }
|
|
426
292
|
:
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
293
|
+
annotation=ANNOTATION simplePath[ $name, 'AnnoDef' ]
|
|
294
|
+
{ if ($outer.kind !== 'source') { // this is a syntax restriction to avoid confusion
|
|
295
|
+
this.error( 'syntax-unexpected-vocabulary', $annotation, { '#': $outer.kind } );
|
|
296
|
+
$art = {}; }
|
|
297
|
+
else {
|
|
298
|
+
if (!$outer.vocabularies) $outer.vocabularies = Object.create(null);
|
|
299
|
+
this.addDef( $art, $outer, 'vocabularies', 'annotation', $name );
|
|
300
|
+
}
|
|
301
|
+
this.docComment( $art ); }
|
|
302
|
+
annotationAssignment_fix[ $art ]*
|
|
303
|
+
typeSpecSemi[ $art ] // also 'includes'...
|
|
304
|
+
;
|
|
305
|
+
|
|
306
|
+
serviceDef[ art, outer, defOnly = false ] locals[ name = {} ]
|
|
307
|
+
@after { this.attachLocation( $art ); }
|
|
308
|
+
:
|
|
309
|
+
SERVICE simplePath[ $name, 'Service' ]
|
|
310
|
+
{ this.addDef( $art, $outer, 'artifacts', 'service', $name ); }
|
|
311
|
+
optArtifactsBlock[ $art, defOnly ]
|
|
312
|
+
;
|
|
313
|
+
|
|
314
|
+
contextDef[ art, outer, defOnly = false ] locals[ name = {} ]
|
|
315
|
+
@after { this.attachLocation( $art ); }
|
|
316
|
+
:
|
|
317
|
+
CONTEXT simplePath[ $name, 'Context' ]
|
|
318
|
+
{ this.addDef( $art, $outer, 'artifacts', 'context', $name ); }
|
|
319
|
+
optArtifactsBlock[ $art, defOnly ]
|
|
320
|
+
;
|
|
321
|
+
|
|
322
|
+
eventDef[ art, outer ] locals[ name = {} ]
|
|
323
|
+
@after { /* #ATN 1 */ this.attachLocation( $art ); }
|
|
324
|
+
:
|
|
325
|
+
EVENT simplePath[ $name, 'Event' ]
|
|
326
|
+
{ this.addDef( $art, $outer, 'artifacts', 'event', $name );
|
|
327
|
+
this.docComment( $art ); }
|
|
328
|
+
annotationAssignment_fix[ $art ]*
|
|
433
329
|
(
|
|
434
|
-
|
|
435
|
-
artifactDef[ $art, $service ? 'SERVICE' : 'CONTEXT' ]*
|
|
436
|
-
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
437
|
-
optionalSemi
|
|
330
|
+
typeStruct[ $art ] optionalSemi
|
|
438
331
|
|
|
|
439
|
-
|
|
332
|
+
':'
|
|
333
|
+
// #ATN: includeRef can be / start with PROJECTION
|
|
334
|
+
(
|
|
335
|
+
{ $art.type = {}; }
|
|
336
|
+
simplePath[ $art.type, 'artref' ]
|
|
337
|
+
(
|
|
338
|
+
{ $art.includes = [ $art.type ]; delete $art.type; }
|
|
339
|
+
( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
|
|
340
|
+
includeRef[ $art ]
|
|
341
|
+
)*
|
|
342
|
+
typeStruct[ $art ] optionalSemi
|
|
343
|
+
|
|
|
344
|
+
{ this.docComment( $art ); }
|
|
345
|
+
annotationAssignment_ll1[ $art ]*
|
|
346
|
+
requiredSemi
|
|
347
|
+
)
|
|
348
|
+
|
|
|
349
|
+
typeStruct[ $art ] optionalSemi
|
|
350
|
+
|
|
|
351
|
+
qp=projectionSpec
|
|
352
|
+
{ $art.query = $qp.query; $art['$'+'syntax'] = 'projection'; }
|
|
353
|
+
optionalSemi // TODO: not fully correct without columns or excluding
|
|
354
|
+
)
|
|
440
355
|
)
|
|
441
356
|
;
|
|
442
357
|
|
|
358
|
+
viewDef[ art, outer ] locals[ name = {} ]
|
|
359
|
+
@after { this.attachLocation( $art ); }
|
|
360
|
+
:
|
|
361
|
+
v=VIEW simplePath[ $name, 'Entity' ]
|
|
362
|
+
{ $art['$'+'syntax'] = 'view';
|
|
363
|
+
this.addDef( $art, $outer, 'artifacts', 'entity', $name );
|
|
364
|
+
this.docComment( $art ); }
|
|
365
|
+
annotationAssignment_fix[ $art ]*
|
|
366
|
+
(
|
|
367
|
+
parameterListDef[ $art ]
|
|
368
|
+
|
|
|
369
|
+
// TODO: warning deprecated?
|
|
370
|
+
( HideAlternatives | WITH ) { $art.params = this.createDict(); }
|
|
371
|
+
PARAMETERS
|
|
372
|
+
parameterDef[ $art ]
|
|
373
|
+
( ',' parameterDef[ $art ] )* // no optional final ',' here
|
|
374
|
+
{ this.finalizeDictOrArray( $art.params ); }
|
|
375
|
+
)?
|
|
376
|
+
AS qe=queryExpression { $art.query = $qe.query; }
|
|
377
|
+
// TODO check ANTLR: bad msg with 'view V as'<eof> but 'view V as FOO' is fine
|
|
378
|
+
requiredSemi
|
|
379
|
+
;
|
|
380
|
+
|
|
443
381
|
entityDef[ art, outer ] locals[ name = {} ]
|
|
444
382
|
@after { this.attachLocation( $art ); }
|
|
445
383
|
:
|
|
@@ -458,7 +396,6 @@ entityDef[ art, outer ] locals[ name = {} ]
|
|
|
458
396
|
'{' { $art.elements = this.createDict(); }
|
|
459
397
|
elementDef[ $art ]*
|
|
460
398
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
461
|
-
// TODO: action definitions in a specific section?
|
|
462
399
|
(
|
|
463
400
|
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
464
401
|
actionFunctionDef[ $art ]*
|
|
@@ -489,161 +426,48 @@ entityDef[ art, outer ] locals[ name = {} ]
|
|
|
489
426
|
)
|
|
490
427
|
;
|
|
491
428
|
|
|
492
|
-
|
|
493
|
-
@after { this.attachLocation($
|
|
429
|
+
aspectDef[ art, outer ] locals[ name = {} ]
|
|
430
|
+
@after { this.attachLocation( $art ); }
|
|
494
431
|
:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
{
|
|
498
|
-
$
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
432
|
+
( ASPECT | ( abs=ABSTRACT | HideAlternatives ) ent=ENTITY )
|
|
433
|
+
simplePath[ $name, 'Type' ]
|
|
434
|
+
{ this.addDef( $art, $outer, 'artifacts', 'aspect', $name );
|
|
435
|
+
if ($ent)
|
|
436
|
+
this.warning( 'syntax-deprecated-abstract', this.tokenLocation( $abs, $ent ) );
|
|
437
|
+
this.docComment( $art ); }
|
|
438
|
+
annotationAssignment_fix[ $art ]*
|
|
502
439
|
( ':'
|
|
503
|
-
{ $src.scope = $src.path.length; }
|
|
504
|
-
fromPath[ $src, 'ref']
|
|
505
|
-
)?
|
|
506
|
-
( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
|
|
507
|
-
// ANTLR errors are better if we use ( A )? instead of ( A | ):
|
|
508
|
-
{ if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
|
|
509
|
-
bracedSelectItemListDef[ $query, 'columns' ]?
|
|
510
|
-
excludingClause[ $query ]?
|
|
511
|
-
;
|
|
512
|
-
|
|
513
|
-
projectionClauses[ query ]
|
|
514
|
-
@after { this.attachLocation($query); }
|
|
515
|
-
:
|
|
516
|
-
( WHERE cond=condition { $query.where = $cond.cond; } )?
|
|
517
|
-
(
|
|
518
|
-
GROUP BY
|
|
519
|
-
e1=expression { $query.groupBy = [ $e1.expr ]; }
|
|
520
|
-
( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
|
|
521
|
-
)?
|
|
522
|
-
( HAVING having=condition { $query.having = $having.cond; } )?
|
|
523
|
-
( ob=orderByClause[ $query ] { $query = $ob.query; } ) ?
|
|
524
|
-
( lc=limitClause[ $query ] { $query = $lc.query; } ) ?
|
|
525
|
-
;
|
|
526
|
-
|
|
527
|
-
excludingClause[ query ]
|
|
528
|
-
:
|
|
529
|
-
// syntax is less than ideal - EXCLUDING is only useful for `*` - with
|
|
530
|
-
// this syntax, people wonder what happens with explicit select items
|
|
531
|
-
EXCLUDING '{' { $query.excludingDict = this.createDict(); }
|
|
532
|
-
projectionExclusion[ $query ]
|
|
533
|
-
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
534
|
-
projectionExclusion[ $query ]
|
|
535
|
-
)*
|
|
536
|
-
'}' { this.finalizeDictOrArray( $query.excludingDict ); }
|
|
537
|
-
;
|
|
538
|
-
|
|
539
|
-
projectionExclusion[ outer ] locals[ art = {} ]
|
|
540
|
-
@after { this.attachLocation($art); }
|
|
541
|
-
:
|
|
542
|
-
name=ident['ref']
|
|
543
|
-
{ this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
|
|
544
|
-
;
|
|
545
|
-
|
|
546
|
-
// also used for aspect
|
|
547
|
-
extendEntity[ art, outer ] locals[ name = {} ]
|
|
548
|
-
@after { /* #ATN 1 */ this.attachLocation( $art ); }
|
|
549
|
-
:
|
|
550
|
-
kind=(ASPECT | ENTITY) simplePath[ $name, 'Extend' ]
|
|
551
|
-
{ $art.expectedKind = $kind.text.toLowerCase(); $art.name = $name;
|
|
552
|
-
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
553
|
-
}
|
|
554
|
-
(
|
|
555
|
-
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
556
|
-
annotationAssignment_ll1[ $art ]*
|
|
557
|
-
// ATN: the ref can start with ACTIONS
|
|
558
|
-
(
|
|
559
|
-
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
560
|
-
requiredSemi
|
|
561
|
-
|
|
|
562
|
-
extendForEntity[ $art ]
|
|
563
|
-
)
|
|
564
|
-
|
|
|
565
|
-
{ this.docComment( $art ); }
|
|
566
|
-
annotationAssignment_ll1[ $art ]*
|
|
567
|
-
extendForEntity[ $art ]
|
|
568
|
-
)
|
|
569
|
-
;
|
|
570
|
-
|
|
571
|
-
extendForEntity[ art ]
|
|
572
|
-
:
|
|
573
|
-
'{' { $art.elements = this.createDict(); }
|
|
574
|
-
elementDefOrExtend[ $art ]*
|
|
575
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
576
|
-
(
|
|
577
|
-
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
578
|
-
actionFunctionDef[ $art ]*
|
|
579
|
-
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
580
|
-
)?
|
|
581
|
-
optionalSemi
|
|
582
|
-
|
|
|
583
|
-
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
584
|
-
actionFunctionDef[ $art ]*
|
|
585
|
-
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
586
|
-
optionalSemi
|
|
587
|
-
|
|
|
588
|
-
requiredSemi
|
|
589
|
-
;
|
|
590
|
-
|
|
591
|
-
extendProjection[ art, outer ] locals[ name = {} ]
|
|
592
|
-
@after { this.attachLocation( $art ); }
|
|
593
|
-
:
|
|
594
|
-
expected=PROJECTION simplePath[ $name, 'Extend' ]
|
|
595
|
-
{ $art.expectedKind = 'entity'; $art.name = $name;
|
|
596
|
-
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
597
|
-
}
|
|
598
|
-
( WITH { this.noSemicolonHere(); } )?
|
|
599
|
-
{ this.docComment( $art ); }
|
|
600
|
-
annotationAssignment_ll1[ $art ]*
|
|
601
|
-
(
|
|
602
|
-
'{' { $art.columns = []; }
|
|
603
440
|
(
|
|
604
|
-
|
|
605
|
-
( ',' { if (this.isStraightBefore(
|
|
606
|
-
|
|
441
|
+
includeRef[ $art ]
|
|
442
|
+
( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
|
|
443
|
+
includeRef[ $art ]
|
|
607
444
|
)*
|
|
608
445
|
)?
|
|
609
|
-
|
|
446
|
+
)?
|
|
447
|
+
( // `aspect MyAspect {};`
|
|
448
|
+
'{' { $art.elements = this.createDict(); }
|
|
449
|
+
( elementDef[ $art ]* )
|
|
450
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
610
451
|
(
|
|
611
452
|
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
612
453
|
actionFunctionDef[ $art ]*
|
|
613
454
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
614
455
|
)?
|
|
615
456
|
optionalSemi
|
|
616
|
-
|
|
|
617
|
-
|
|
618
|
-
actionFunctionDef[ $art ]*
|
|
619
|
-
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
620
|
-
optionalSemi
|
|
621
|
-
|
|
|
457
|
+
| // `aspect MyAspect;`, e.g. for annotation aspects.
|
|
458
|
+
{ this.aspectWithoutElements( $art ); }
|
|
622
459
|
requiredSemi
|
|
623
460
|
)
|
|
624
461
|
;
|
|
625
462
|
|
|
626
|
-
|
|
627
|
-
actionFunctionDef[ outer ] locals[ art = {} ]
|
|
463
|
+
typeDef[ art, outer ] locals[ name = {} ]
|
|
628
464
|
@after { this.attachLocation( $art ); }
|
|
629
465
|
:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
this.docComment( $art ); }
|
|
636
|
-
annotationAssignment_fix[ $art ]*
|
|
637
|
-
parameterListDef[ $art ]
|
|
638
|
-
( returnTypeSpec[ $art ] | requiredSemi )
|
|
639
|
-
|
|
|
640
|
-
FUNCTION name=ident['BoundAction']
|
|
641
|
-
{ this.addDef( $art, $outer, 'actions', 'function', $name.id );
|
|
642
|
-
this.docComment( $art ); }
|
|
643
|
-
annotationAssignment_fix[ $art ]*
|
|
644
|
-
parameterListDef[ $art ]
|
|
645
|
-
returnTypeSpec[ $art ]
|
|
646
|
-
)
|
|
466
|
+
TYPE simplePath[ $name, 'Type' ]
|
|
467
|
+
{ this.addDef( $art, $outer, 'artifacts', 'type', $name );
|
|
468
|
+
this.docComment( $art ); }
|
|
469
|
+
annotationAssignment_fix[ $art ]*
|
|
470
|
+
typeSpecSemi[ $art ]
|
|
647
471
|
;
|
|
648
472
|
|
|
649
473
|
actionFunctionMainDef[ art, outer ] locals[ name = {} ]
|
|
@@ -664,127 +488,273 @@ actionFunctionMainDef[ art, outer ] locals[ name = {} ]
|
|
|
664
488
|
returnTypeSpec[ $art ]
|
|
665
489
|
;
|
|
666
490
|
|
|
667
|
-
|
|
668
|
-
|
|
491
|
+
// Member definitions: actions, elements, enums, parameters: --------------------
|
|
492
|
+
|
|
493
|
+
actionFunctionDef[ outer ] locals[ art = {} ]
|
|
494
|
+
@after { this.attachLocation( $art ); }
|
|
669
495
|
:
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
this.docComment( $art ); }
|
|
673
|
-
annotationAssignment_fix[ $art ]*
|
|
496
|
+
{ $art.location = this.startLocation();; this.docComment( $art ); }
|
|
497
|
+
annotationAssignment_ll1[ $art ]*
|
|
674
498
|
(
|
|
675
|
-
|
|
499
|
+
ACTION name=ident['BoundAction']
|
|
500
|
+
{ this.addDef( $art, $outer, 'actions', 'action', $name.id );
|
|
501
|
+
this.docComment( $art ); }
|
|
502
|
+
annotationAssignment_fix[ $art ]*
|
|
503
|
+
parameterListDef[ $art ]
|
|
504
|
+
( returnTypeSpec[ $art ] | requiredSemi )
|
|
676
505
|
|
|
|
677
|
-
'
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
{ $art.includes = [ $art.type ]; delete $art.type; }
|
|
684
|
-
( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
|
|
685
|
-
includeRef[ $art ]
|
|
686
|
-
)*
|
|
687
|
-
typeStruct[ $art ] optionalSemi
|
|
688
|
-
|
|
|
689
|
-
{ this.docComment( $art ); }
|
|
690
|
-
annotationAssignment_ll1[ $art ]*
|
|
691
|
-
requiredSemi
|
|
692
|
-
)
|
|
693
|
-
|
|
|
694
|
-
typeStruct[ $art ] optionalSemi
|
|
695
|
-
|
|
|
696
|
-
qp=projectionSpec
|
|
697
|
-
{ $art.query = $qp.query; $art['$'+'syntax'] = 'projection'; }
|
|
698
|
-
optionalSemi // TODO: not fully correct without columns or excluding
|
|
699
|
-
|
|
700
|
-
)
|
|
506
|
+
FUNCTION name=ident['BoundAction']
|
|
507
|
+
{ this.addDef( $art, $outer, 'actions', 'function', $name.id );
|
|
508
|
+
this.docComment( $art ); }
|
|
509
|
+
annotationAssignment_fix[ $art ]*
|
|
510
|
+
parameterListDef[ $art ]
|
|
511
|
+
returnTypeSpec[ $art ]
|
|
701
512
|
)
|
|
702
513
|
;
|
|
703
514
|
|
|
704
|
-
|
|
515
|
+
parameterDef[ outer ] locals[ art = {} ]
|
|
705
516
|
@after { this.attachLocation( $art ); }
|
|
706
517
|
:
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
if ($ent)
|
|
712
|
-
this.warning( 'syntax-deprecated-abstract', this.tokenLocation( $abs, $ent ), {},
|
|
713
|
-
'Abstract entity definitions are deprecated; use aspect definitions instead' );
|
|
518
|
+
{ this.docComment( $art ); }
|
|
519
|
+
annotationAssignment_ll1[ $art ]*
|
|
520
|
+
name=ident['Param']
|
|
521
|
+
{ this.addDef( $art, $outer, 'params', 'param', $name.id );
|
|
714
522
|
this.docComment( $art ); }
|
|
715
523
|
annotationAssignment_fix[ $art ]*
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
includeRef[ $art ]
|
|
721
|
-
)*
|
|
722
|
-
)?
|
|
723
|
-
)?
|
|
724
|
-
( // `aspect MyAspect {};`
|
|
725
|
-
'{' { $art.elements = this.createDict(); }
|
|
726
|
-
( elementDef[ $art ]* )
|
|
727
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
728
|
-
// TODO: action definitions in a specific section?
|
|
729
|
-
(
|
|
730
|
-
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
731
|
-
actionFunctionDef[ $art ]*
|
|
732
|
-
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
733
|
-
)?
|
|
734
|
-
optionalSemi
|
|
735
|
-
| // `aspect MyAspect;`, e.g. for annotation aspects.
|
|
736
|
-
{ this.aspectWithoutElements( $art ); }
|
|
737
|
-
requiredSemi
|
|
738
|
-
)
|
|
524
|
+
typeSpec[ $art ]
|
|
525
|
+
defaultValue[ $art ]?
|
|
526
|
+
{ this.docComment( $art ); }
|
|
527
|
+
annotationAssignment_ll1[ $art ]*
|
|
739
528
|
;
|
|
740
529
|
|
|
741
|
-
|
|
742
|
-
@after { this.attachLocation( $art ); }
|
|
530
|
+
parameterListDef[ art ]
|
|
743
531
|
:
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
532
|
+
'(' { $art.params = this.createDict(); }
|
|
533
|
+
// also empty param list (we might do some hacking later to allow reserved words)
|
|
534
|
+
// see annotationAssignment_paren
|
|
535
|
+
(
|
|
536
|
+
parameterDef[ $art ]
|
|
537
|
+
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
538
|
+
parameterDef[ $art ]
|
|
539
|
+
)*
|
|
540
|
+
)?
|
|
541
|
+
')' { this.finalizeDictOrArray( $art.params ); }
|
|
749
542
|
;
|
|
750
543
|
|
|
751
|
-
|
|
544
|
+
enumSymbolDef[ outer ] locals[ art = {} ]
|
|
752
545
|
@after { this.attachLocation( $art ); }
|
|
753
546
|
:
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
547
|
+
{ $art.location = this.startLocation();; this.docComment( $art ); }
|
|
548
|
+
annotationAssignment_ll1[ $art ]*
|
|
549
|
+
name=ident['Enum']
|
|
550
|
+
{ this.addDef( $art, $outer, 'enum', 'enum', $name.id );
|
|
551
|
+
this.docComment( $art ); }
|
|
552
|
+
annotationAssignment_ll1[ $art ]*
|
|
553
|
+
( '='
|
|
554
|
+
{ this.excludeExpected( ['Boolean', 'QuotedLiteral', "'#'", 'NULL'] ); }
|
|
555
|
+
(
|
|
556
|
+
val=literalValue
|
|
557
|
+
{ $art.value = $val.val; }
|
|
558
|
+
|
|
|
559
|
+
( plus='+' | min='-' ) num=Number
|
|
560
|
+
{ $art.value = this.numberLiteral( $num, $plus||$min ); }
|
|
561
|
+
)
|
|
562
|
+
{ this.docComment( $art ); }
|
|
563
|
+
annotationAssignment_ll1[ $art ]*
|
|
564
|
+
)?
|
|
565
|
+
requiredSemi
|
|
760
566
|
;
|
|
761
567
|
|
|
762
|
-
|
|
763
|
-
@after { this.attachLocation( $art ); }
|
|
568
|
+
elementDef[ outer ] locals[ $art = {} ]
|
|
764
569
|
:
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
570
|
+
{ $art.location = this.startLocation();; this.docComment( $art ); }
|
|
571
|
+
annotationAssignment_ll1[ $art ]*
|
|
572
|
+
elementDefInner[ $art, $outer, false ]
|
|
573
|
+
;
|
|
574
|
+
|
|
575
|
+
elementDefInner[ art, outer, allowEq ]
|
|
576
|
+
@after{ this.attachLocation( $art ); }
|
|
577
|
+
:
|
|
578
|
+
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
579
|
+
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[:{@=}]$/ ); }
|
|
580
|
+
( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
|
|
581
|
+
( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
|
|
582
|
+
{ this.setLocalToken( 'MASKED', 'MASKED', /^[:{@=}]$/ ); }
|
|
583
|
+
( masked=MASKED
|
|
584
|
+
{
|
|
585
|
+
$art.masked = this.valueWithTokenLocation( true, $masked ) ;
|
|
586
|
+
this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
|
|
772
587
|
}
|
|
588
|
+
)?
|
|
589
|
+
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^[:{@=}]$/ ); }
|
|
590
|
+
ELEMENT?
|
|
591
|
+
name=ident['Element']
|
|
592
|
+
{ this.addDef( $art, $outer, 'elements', 'element', $name.id );
|
|
773
593
|
this.docComment( $art ); }
|
|
774
594
|
annotationAssignment_fix[ $art ]*
|
|
775
|
-
|
|
595
|
+
// TODO: we can think of making the typeSpec optional and do checks instead:
|
|
596
|
+
// type optional with '=', type required otherwise
|
|
597
|
+
(
|
|
598
|
+
typeStruct[ $art ]
|
|
599
|
+
( nullability[ $art ]
|
|
600
|
+
requiredSemi
|
|
601
|
+
| optionalSemi // NOT and NULL are reserved...
|
|
602
|
+
)
|
|
603
|
+
|
|
|
604
|
+
':'
|
|
605
|
+
elementType[ $art ]
|
|
606
|
+
|
|
|
607
|
+
// this is also called for enum symbols (in EXTEND)
|
|
608
|
+
eq='=' e=expression // never introduce AS as syntax variant of '='
|
|
609
|
+
{
|
|
610
|
+
if (!$allowEq || $e.expr && !$e.expr.literal )
|
|
611
|
+
this.error( 'syntax-unsupported-calc-elem', $eq );
|
|
612
|
+
else if ($e.expr)
|
|
613
|
+
$art.value = $e.expr;
|
|
614
|
+
}
|
|
615
|
+
{ this.docComment( $art ); }
|
|
616
|
+
annotationAssignment_ll1[ $art ]* // for enum symbol def via EXTEND
|
|
617
|
+
requiredSemi
|
|
618
|
+
)
|
|
619
|
+
;
|
|
620
|
+
|
|
621
|
+
elementType[ art ] // TODO: split this monster rule
|
|
622
|
+
@after{ /* #ATN 3 */ this.attachLocation( $art ); }
|
|
623
|
+
:
|
|
624
|
+
// #ATN: referenced type name can be ASSOCIATION or ARRAY or TYPE or LOCALIZED
|
|
625
|
+
typeStruct[ $art ]
|
|
626
|
+
nullability[ $art ]?
|
|
627
|
+
requiredSemi
|
|
628
|
+
|
|
|
629
|
+
typeAssociationBase[ $art, true ]
|
|
630
|
+
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
631
|
+
(
|
|
632
|
+
typeStruct[ $art.target, true ] optionalSemi
|
|
633
|
+
|
|
|
634
|
+
one=ONE
|
|
635
|
+
{ this.setMaxCardinality( $art, $one, this.numberLiteral( $one, null, '1' ) ); }
|
|
636
|
+
typeCompoStruct[ $art.target ] optionalSemi
|
|
637
|
+
|
|
|
638
|
+
many=MANY
|
|
639
|
+
{ this.setMaxCardinality( $art, $many, { literal: 'string', val: '*' } ); }
|
|
640
|
+
typeCompoStruct[ $art.target ] optionalSemi
|
|
641
|
+
|
|
|
642
|
+
// we do not support `Composition of many { e }` - ambiguity ad-hoc target versus foreign keys!
|
|
643
|
+
typeToMany[ $art ] typeAssociationElementCont[ $art ]
|
|
644
|
+
|
|
|
645
|
+
typeToOne[ $art ] typeAssociationElementCont[ $art ]
|
|
646
|
+
|
|
|
647
|
+
simplePath[ $art.target, 'artref' ] typeAssociationElementCont[ $art ]
|
|
648
|
+
)
|
|
649
|
+
|
|
|
650
|
+
(
|
|
651
|
+
array=ARRAY of=OF
|
|
652
|
+
{ $art.items = { location: this.tokenLocation( $array, $of ) }; }
|
|
653
|
+
| many=MANY
|
|
654
|
+
{ $art.items = { location: this.tokenLocation( $many ) };}
|
|
655
|
+
)
|
|
656
|
+
( typeStruct[ $art.items ]
|
|
657
|
+
nullability[ $art.items ]?
|
|
658
|
+
| // #ATN: typeRefOptArgs/typeTypeOf can start with TYPE
|
|
659
|
+
( typeTypeOf[ $art.items ] | typeRefOptArgs[ $art.items ] )
|
|
660
|
+
nullability[ $art.items ]? // only if not followed by `enum`
|
|
661
|
+
{ this.docComment( $art ); }
|
|
662
|
+
annotationAssignment_ll1[ $art ]*
|
|
663
|
+
(
|
|
664
|
+
{ if ($art.items.notNull) {
|
|
665
|
+
this.message( 'syntax-unexpected-null', $art.items.notNull.location,
|
|
666
|
+
{ keyword: $art.items.notNull.val ? 'not null' : 'null' } );
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
ENUM '{' { $art.items.enum = this.createDict(); }
|
|
670
|
+
enumSymbolDef[ $art.items ]*
|
|
671
|
+
'}' { this.finalizeDictOrArray( $art.items.enum ); }
|
|
672
|
+
nullability[ $art.items ]?
|
|
673
|
+
)?
|
|
674
|
+
)
|
|
675
|
+
requiredSemi // also req after struct/enum
|
|
676
|
+
|
|
|
677
|
+
l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
678
|
+
typeRefOptArgs[ $art ]
|
|
679
|
+
{ this.docComment( $art ); }
|
|
680
|
+
annotationAssignment_ll1[ $art ]*
|
|
681
|
+
( elementProperties[ $art ]
|
|
682
|
+
{ this.docComment( $art ); }
|
|
683
|
+
annotationAssignment_ll1[ $art ]*
|
|
684
|
+
)?
|
|
685
|
+
requiredSemi
|
|
686
|
+
|
|
|
687
|
+
typeTypeOf[ $art ] // Note: Same as the typeRefOptArgs rule below
|
|
688
|
+
{ this.docComment( $art ); }
|
|
689
|
+
annotationAssignment_ll1[ $art ]*
|
|
690
|
+
(
|
|
691
|
+
ENUM '{' { $art.enum = this.createDict(); }
|
|
692
|
+
enumSymbolDef[ $art ]*
|
|
693
|
+
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
694
|
+
elementProperties[ $art ]?
|
|
695
|
+
|
|
|
696
|
+
elementProperties[ $art ]
|
|
697
|
+
{ this.docComment( $art ); }
|
|
698
|
+
annotationAssignment_ll1[ $art ]*
|
|
699
|
+
)?
|
|
700
|
+
requiredSemi // also req after foreign key spec
|
|
701
|
+
|
|
|
702
|
+
typeRefOptArgs[ $art ] // Note: Same as the typeTypeOf rule above
|
|
703
|
+
{ this.docComment( $art ); }
|
|
704
|
+
annotationAssignment_ll1[ $art ]*
|
|
705
|
+
(
|
|
706
|
+
ENUM '{' { $art.enum = this.createDict(); }
|
|
707
|
+
enumSymbolDef[ $art ]*
|
|
708
|
+
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
709
|
+
elementProperties[ $art ]?
|
|
710
|
+
|
|
|
711
|
+
elementProperties[ $art ]
|
|
712
|
+
{ this.docComment( $art ); }
|
|
713
|
+
annotationAssignment_ll1[ $art ]*
|
|
714
|
+
)?
|
|
715
|
+
requiredSemi // also req after enum spec
|
|
716
|
+
;
|
|
717
|
+
|
|
718
|
+
elementProperties[ elem ]
|
|
719
|
+
:
|
|
720
|
+
nullability[ $elem ]
|
|
721
|
+
defaultValue[ $elem ]?
|
|
722
|
+
|
|
|
723
|
+
defaultValue[ $elem ]
|
|
724
|
+
nullability[ $elem ]?
|
|
725
|
+
|
|
|
726
|
+
eq='='
|
|
727
|
+
{ this.error( 'syntax-unsupported-calc-elem', $eq ); }
|
|
728
|
+
e=expression { $elem.value = $e.expr; }
|
|
729
|
+
;
|
|
730
|
+
|
|
731
|
+
defaultValue[ art ] locals[ elem, elements = {} ]
|
|
732
|
+
:
|
|
733
|
+
// TODO: We may support structured default values here.
|
|
734
|
+
DEFAULT expr=expression { $art.default = $expr.expr; }
|
|
776
735
|
;
|
|
777
736
|
|
|
737
|
+
// Extend and annotate ----------------------------------------------------------
|
|
738
|
+
|
|
778
739
|
extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
779
740
|
@after{ /* #ATN 1 */ this.attachLocation( $art ); }
|
|
780
741
|
:
|
|
781
742
|
simplePath[ $name, 'Extend' ]
|
|
782
|
-
( ':' simplePath[ $elemName, 'Element'] )?
|
|
783
|
-
{ this.addExtension( $art, $outer, 'extend', $name, $elemName.path ); }
|
|
784
743
|
(
|
|
785
|
-
|
|
744
|
+
':' simplePath[ $elemName, 'Element']
|
|
745
|
+
{ this.addExtension( $art, $outer, 'extend', $name, $elemName.path ); }
|
|
746
|
+
extendWithOptElementsOrType[ art ]
|
|
747
|
+
|
|
|
748
|
+
{ this.addExtension( $art, $outer, 'extend', $name ); }
|
|
749
|
+
extendWithOptElementsNoWith[ art ]
|
|
750
|
+
|
|
|
751
|
+
{ this.addExtension( $art, $outer, 'extend', $name ); }
|
|
752
|
+
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
786
753
|
annotationAssignment_ll1[ $art ]*
|
|
754
|
+
// #ATN: ELEMENTS, ENUM, DEFINITIONS, COLUMNS, ACTIONS are not reserved and
|
|
755
|
+
// could be includeRef
|
|
787
756
|
(
|
|
757
|
+
// all the alternatives from `extendWithOptElementsOrType` --------------
|
|
788
758
|
'{' { $art.elements = this.createDict(); }
|
|
789
759
|
elementDefOrExtend[ $art ]*
|
|
790
760
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
@@ -792,32 +762,34 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
792
762
|
optionalSemi
|
|
793
763
|
|
|
|
794
764
|
requiredSemi
|
|
795
|
-
)
|
|
796
|
-
|
|
|
797
|
-
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
798
|
-
annotationAssignment_ll1[ $art ]*
|
|
799
|
-
// #ATN: DEFINITIONS, COLUMNS, ACTIONS etc are not reserved and could be identifiers (ref).
|
|
800
|
-
// TODO: exclude "expected" according to disallowElementExtension()
|
|
801
|
-
(
|
|
802
|
-
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
803
|
-
requiredSemi
|
|
804
765
|
|
|
|
805
|
-
'{' { $art.elements = this.createDict(); }
|
|
766
|
+
ELEMENTS '{' { $art.elements = this.createDict(); }
|
|
806
767
|
elementDefOrExtend[ $art ]*
|
|
807
768
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
808
|
-
|
|
769
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
809
770
|
optionalSemi
|
|
810
771
|
|
|
|
772
|
+
ENUM '{' { $art.enum = this.createDict(); }
|
|
773
|
+
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
774
|
+
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
775
|
+
optionalSemi
|
|
776
|
+
|
|
|
777
|
+
// extend Art with (length: 10);
|
|
778
|
+
// `with` is required, or we could have `extend String(length:10);`.
|
|
779
|
+
// future `extend Action with (param: Type)` now has ambiguity
|
|
780
|
+
typeNamedArgList[ $art ]
|
|
781
|
+
requiredSemi
|
|
782
|
+
|
|
|
783
|
+
// extension alternatives for main definitions --------------------------
|
|
784
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
811
785
|
requiredSemi
|
|
812
786
|
|
|
|
813
|
-
{ this.disallowElementExtension( $elemName, $outer, 'definitions' ); }
|
|
814
787
|
DEFINITIONS
|
|
815
788
|
'{' { $art.artifacts = this.createDict(); }
|
|
816
|
-
|
|
789
|
+
artifactDefOrExtend[ $art, true ]*
|
|
817
790
|
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
818
791
|
optionalSemi
|
|
819
792
|
|
|
|
820
|
-
{ this.disallowElementExtension( $elemName, $outer, 'columns' ); }
|
|
821
793
|
COLUMNS
|
|
822
794
|
'{' { $art.columns = []; }
|
|
823
795
|
(
|
|
@@ -829,85 +801,239 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
829
801
|
'}'
|
|
830
802
|
optionalSemi
|
|
831
803
|
|
|
|
832
|
-
{ this.disallowElementExtension( $elemName, $outer, 'actions' ); }
|
|
833
804
|
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
834
805
|
actionFunctionDef[ $art ]* // TODO: no EXTEND in actions? (ok, would just allow annos)
|
|
835
806
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
836
807
|
optionalSemi
|
|
837
|
-
|
|
|
838
|
-
ELEMENTS '{' { $art.elements = this.createDict(); }
|
|
839
|
-
elementDefOrExtend[ $art ]*
|
|
840
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
841
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
842
|
-
optionalSemi
|
|
843
|
-
|
|
|
844
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
845
|
-
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
846
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
847
|
-
optionalSemi
|
|
848
|
-
|
|
|
849
|
-
// extend Art with (length: 10);
|
|
850
|
-
// `with` is required, or we could have `extend String(length:10);`.
|
|
851
|
-
typeNamedArgList[ $art ]
|
|
852
|
-
requiredSemi
|
|
853
808
|
)
|
|
809
|
+
// TODO: what about adding both ELEMENTS and ACTIONS? (TODO: csn input test & to-cdl)
|
|
854
810
|
)
|
|
855
811
|
;
|
|
856
812
|
|
|
857
|
-
|
|
813
|
+
extendService[ art, outer ] locals[ name = {} ]
|
|
814
|
+
@after { this.attachLocation( $art ); }
|
|
858
815
|
:
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
(
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
|
865
|
-
'{' { $art.elements = this.createDict(); }
|
|
866
|
-
elementDefOrExtend[ $art ]*
|
|
867
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
868
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
869
|
-
optionalSemi
|
|
870
|
-
|
|
|
871
|
-
// extend type|element Art with (length: 10);
|
|
872
|
-
typeNamedArgList[ $art ]
|
|
873
|
-
requiredSemi
|
|
874
|
-
|
|
|
875
|
-
requiredSemi
|
|
876
|
-
)
|
|
877
|
-
|
|
|
878
|
-
{ this.docComment( $art ); }
|
|
879
|
-
annotationAssignment_ll1[ $art ]*
|
|
880
|
-
(
|
|
881
|
-
'{' { $art.elements = this.createDict(); }
|
|
882
|
-
elementDefOrExtend[ $art ]*
|
|
883
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
884
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
885
|
-
optionalSemi
|
|
886
|
-
|
|
|
887
|
-
requiredSemi
|
|
888
|
-
)
|
|
816
|
+
SERVICE { $art.expectedKind = 'service'; }
|
|
817
|
+
simplePath[ $name, 'Service' ]
|
|
818
|
+
{ $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
|
|
819
|
+
( WITH { this.noSemicolonHere(); } )?
|
|
820
|
+
optArtifactsBlock[ art, 'SERVICE' ]
|
|
889
821
|
;
|
|
890
822
|
|
|
891
|
-
|
|
823
|
+
extendContext[ art, outer ] locals[ name = {} ]
|
|
892
824
|
@after { this.attachLocation( $art ); }
|
|
893
825
|
:
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
{ this.
|
|
826
|
+
CONTEXT { $art.expectedKind = 'context'; }
|
|
827
|
+
simplePath[ $name, 'Context' ]
|
|
828
|
+
{ $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
|
|
897
829
|
( WITH { this.noSemicolonHere(); } )?
|
|
898
|
-
|
|
899
|
-
|
|
830
|
+
optArtifactsBlock[ art, 'CONTEXT' ]
|
|
831
|
+
;
|
|
832
|
+
|
|
833
|
+
extendEntityOrAspect[ art, outer ] locals[ name = {} ]
|
|
834
|
+
@after { /* #ATN 1 */ this.attachLocation( $art ); }
|
|
835
|
+
:
|
|
836
|
+
kind=(ASPECT | ENTITY) simplePath[ $name, 'Extend' ]
|
|
837
|
+
{ $art.expectedKind = $kind.text.toLowerCase(); $art.name = $name;
|
|
838
|
+
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
839
|
+
}
|
|
900
840
|
(
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
841
|
+
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
842
|
+
annotationAssignment_ll1[ $art ]*
|
|
843
|
+
// ATN: the ref can start with ACTIONS
|
|
905
844
|
(
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
845
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
846
|
+
requiredSemi
|
|
847
|
+
|
|
|
848
|
+
extendForEntity[ $art ]
|
|
849
|
+
)
|
|
850
|
+
|
|
|
851
|
+
{ this.docComment( $art ); }
|
|
852
|
+
annotationAssignment_ll1[ $art ]*
|
|
853
|
+
extendForEntity[ $art ]
|
|
854
|
+
)
|
|
855
|
+
;
|
|
856
|
+
|
|
857
|
+
extendForEntity[ art ]
|
|
858
|
+
:
|
|
859
|
+
'{' { $art.elements = this.createDict(); }
|
|
860
|
+
elementDefOrExtend[ $art ]*
|
|
861
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
862
|
+
(
|
|
863
|
+
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
864
|
+
actionFunctionDef[ $art ]*
|
|
865
|
+
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
866
|
+
)?
|
|
867
|
+
optionalSemi
|
|
868
|
+
|
|
|
869
|
+
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
870
|
+
actionFunctionDef[ $art ]*
|
|
871
|
+
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
872
|
+
optionalSemi
|
|
873
|
+
|
|
|
874
|
+
requiredSemi
|
|
875
|
+
;
|
|
876
|
+
|
|
877
|
+
extendProjection[ art, outer ] locals[ name = {} ]
|
|
878
|
+
@after { this.attachLocation( $art ); }
|
|
879
|
+
:
|
|
880
|
+
expected=PROJECTION simplePath[ $name, 'Extend' ]
|
|
881
|
+
{ $art.expectedKind = 'entity'; $art.name = $name;
|
|
882
|
+
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
883
|
+
}
|
|
884
|
+
( WITH { this.noSemicolonHere(); } )?
|
|
885
|
+
{ this.docComment( $art ); }
|
|
886
|
+
annotationAssignment_ll1[ $art ]*
|
|
887
|
+
(
|
|
888
|
+
'{' { $art.columns = []; }
|
|
889
|
+
(
|
|
890
|
+
selectItemDef[ $art.columns ]
|
|
891
|
+
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
892
|
+
selectItemDef[ $art.columns ]
|
|
893
|
+
)*
|
|
894
|
+
)?
|
|
895
|
+
'}'
|
|
896
|
+
(
|
|
897
|
+
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
898
|
+
actionFunctionDef[ $art ]*
|
|
899
|
+
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
900
|
+
)?
|
|
901
|
+
optionalSemi
|
|
902
|
+
|
|
|
903
|
+
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
904
|
+
actionFunctionDef[ $art ]*
|
|
905
|
+
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
906
|
+
optionalSemi
|
|
907
|
+
|
|
|
908
|
+
requiredSemi
|
|
909
|
+
)
|
|
910
|
+
;
|
|
911
|
+
|
|
912
|
+
extendType[ art, outer ] locals[ name = {} ]
|
|
913
|
+
@after { this.attachLocation( $art ); }
|
|
914
|
+
:
|
|
915
|
+
TYPE simplePath[ $name, 'Extend' ]
|
|
916
|
+
{ $art.expectedKind = 'type'; $art.name = $name;
|
|
917
|
+
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
918
|
+
}
|
|
919
|
+
// extendWithOptElementsOrType + includeRef:
|
|
920
|
+
(
|
|
921
|
+
extendWithOptElementsNoWith[ art ]
|
|
922
|
+
|
|
|
923
|
+
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
924
|
+
annotationAssignment_ll1[ $art ]*
|
|
925
|
+
(
|
|
926
|
+
'{' { $art.elements = this.createDict(); }
|
|
927
|
+
elementDefOrExtend[ $art ]*
|
|
928
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
929
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
930
|
+
optionalSemi
|
|
931
|
+
|
|
|
932
|
+
// extend type|element Art with (length: 10);
|
|
933
|
+
typeNamedArgList[ $art ]
|
|
934
|
+
requiredSemi
|
|
935
|
+
|
|
|
936
|
+
requiredSemi
|
|
937
|
+
|
|
|
938
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
939
|
+
requiredSemi
|
|
940
|
+
)
|
|
941
|
+
)
|
|
942
|
+
;
|
|
943
|
+
|
|
944
|
+
extendWithOptElementsOrType[ art ]
|
|
945
|
+
:
|
|
946
|
+
extendWithOptElementsNoWith[ art ]
|
|
947
|
+
|
|
|
948
|
+
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
949
|
+
annotationAssignment_ll1[ $art ]*
|
|
950
|
+
(
|
|
951
|
+
'{' { $art.elements = this.createDict(); }
|
|
952
|
+
elementDefOrExtend[ $art ]*
|
|
953
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
954
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
955
|
+
optionalSemi
|
|
956
|
+
|
|
|
957
|
+
ELEMENTS '{' { $art.elements = this.createDict(); }
|
|
958
|
+
elementDefOrExtend[ $art ]*
|
|
959
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
960
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
961
|
+
optionalSemi
|
|
962
|
+
|
|
|
963
|
+
ENUM '{' { $art.enum = this.createDict(); }
|
|
964
|
+
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
965
|
+
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
966
|
+
optionalSemi
|
|
967
|
+
|
|
|
968
|
+
// extend type|element Art with (length: 10);
|
|
969
|
+
typeNamedArgList[ $art ]
|
|
970
|
+
requiredSemi
|
|
971
|
+
|
|
|
972
|
+
requiredSemi
|
|
973
|
+
)
|
|
974
|
+
;
|
|
975
|
+
|
|
976
|
+
extendWithOptElementsNoWith[ art ]
|
|
977
|
+
:
|
|
978
|
+
{ this.docComment( $art ); }
|
|
979
|
+
annotationAssignment_ll1[ $art ]*
|
|
980
|
+
(
|
|
981
|
+
'{' { $art.elements = this.createDict(); }
|
|
982
|
+
elementDefOrExtend[ $art ]*
|
|
983
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
984
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
985
|
+
optionalSemi
|
|
986
|
+
|
|
|
987
|
+
requiredSemi
|
|
988
|
+
)
|
|
989
|
+
;
|
|
990
|
+
|
|
991
|
+
// For `extend … with elements` or `extend entity … with`, `extend aspect … with`,
|
|
992
|
+
// i.e. definitions in { … } are never enums
|
|
993
|
+
elementDefOrExtend[ outer ] locals[ art = {} ]
|
|
994
|
+
@after { /* #ATN 1 */ } // if ($art) this.attachLocation( $art ); }
|
|
995
|
+
:
|
|
996
|
+
{ $art.location = this.startLocation();; this.docComment( $art ); }
|
|
997
|
+
annotationAssignment_ll1[ $art ]*
|
|
998
|
+
// #ATN: element name for definition can be EXTEND
|
|
999
|
+
(
|
|
1000
|
+
EXTEND
|
|
1001
|
+
extendElement[ $art, $outer ]
|
|
1002
|
+
|
|
|
1003
|
+
elementDefInner[ $art, $outer, true ]
|
|
1004
|
+
)
|
|
1005
|
+
;
|
|
1006
|
+
|
|
1007
|
+
extendElement[ art, outer ]
|
|
1008
|
+
@after{ this.attachLocation( $art ); }
|
|
1009
|
+
:
|
|
1010
|
+
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^([:{@=}()]|WITH)$/i ); }
|
|
1011
|
+
( expected=ELEMENT { $art.expectedKind = 'element'; } )?
|
|
1012
|
+
name=ident['Element']
|
|
1013
|
+
{ this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
|
|
1014
|
+
extendWithOptElementsOrType[ $art, $art ]
|
|
1015
|
+
;
|
|
1016
|
+
|
|
1017
|
+
annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
1018
|
+
@after { this.attachLocation( $art ); }
|
|
1019
|
+
:
|
|
1020
|
+
simplePath[ $name, 'Annotate' ]
|
|
1021
|
+
( ':' simplePath[ $elemName, 'Element'] )?
|
|
1022
|
+
{ this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
|
|
1023
|
+
( WITH { this.noSemicolonHere(); } )?
|
|
1024
|
+
{ this.docComment( $art ); }
|
|
1025
|
+
annotationAssignment_ll1[ $art ]*
|
|
1026
|
+
(
|
|
1027
|
+
'{' { $art.elements = this.createDict(); }
|
|
1028
|
+
annotateElement[ $art ]*
|
|
1029
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1030
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
1031
|
+
(
|
|
1032
|
+
ACTIONS
|
|
1033
|
+
'{' { $art.actions = this.createDict(); }
|
|
1034
|
+
annotateAction[ $art ]*
|
|
1035
|
+
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
1036
|
+
{ this.checkExtensionDict( $art.actions ); }
|
|
911
1037
|
)?
|
|
912
1038
|
optionalSemi
|
|
913
1039
|
|
|
|
@@ -1008,458 +1134,44 @@ annotateParam [ outer ] locals [ art = {} ]
|
|
|
1008
1134
|
annotationAssignment_ll1[ $art ]*
|
|
1009
1135
|
;
|
|
1010
1136
|
|
|
1011
|
-
//
|
|
1012
|
-
|
|
1013
|
-
enumSymbolDef[ outer ] locals[ art = {} ]
|
|
1014
|
-
@after { this.attachLocation( $art ); }
|
|
1015
|
-
:
|
|
1016
|
-
{ $art.location = this.startLocation();; this.docComment( $art ); }
|
|
1017
|
-
annotationAssignment_ll1[ $art ]*
|
|
1018
|
-
name=ident['Enum']
|
|
1019
|
-
{ this.addDef( $art, $outer, 'enum', 'enum', $name.id );
|
|
1020
|
-
this.docComment( $art ); }
|
|
1021
|
-
annotationAssignment_ll1[ $art ]*
|
|
1022
|
-
( '='
|
|
1023
|
-
{ this.excludeExpected( ['Boolean', 'QuotedLiteral', "'#'", 'NULL'] ); }
|
|
1024
|
-
(
|
|
1025
|
-
val=literalValue
|
|
1026
|
-
{ $art.value = $val.val; }
|
|
1027
|
-
|
|
|
1028
|
-
( plus='+' | min='-' ) num=Number
|
|
1029
|
-
{ $art.value = this.numberLiteral( $num, $plus||$min ); }
|
|
1030
|
-
)
|
|
1031
|
-
{ this.docComment( $art ); }
|
|
1032
|
-
annotationAssignment_ll1[ $art ]*
|
|
1033
|
-
)?
|
|
1034
|
-
requiredSemi
|
|
1035
|
-
;
|
|
1137
|
+
// Type expressions -------------------------------------------------------------
|
|
1036
1138
|
|
|
1037
|
-
|
|
1139
|
+
includeRef[ art ] locals[ incl = {} ]
|
|
1038
1140
|
:
|
|
1039
|
-
|
|
1040
|
-
|
|
1141
|
+
simplePath[ $incl, 'artref' ]
|
|
1142
|
+
{ if ($art.includes) $art.includes.push($incl); else $art.includes = [$incl]; }
|
|
1041
1143
|
;
|
|
1042
1144
|
|
|
1043
|
-
|
|
1044
|
-
@after
|
|
1145
|
+
typeSpec[ art ] // for parameterDef
|
|
1146
|
+
@after{ /* #ATN 1 */ }
|
|
1045
1147
|
:
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
|
1053
|
-
|
|
1148
|
+
typeStruct[ $art ]
|
|
1149
|
+
|
|
|
1150
|
+
':'
|
|
1151
|
+
// #ATN: typeSimple can start with ARRAY or TYPE
|
|
1152
|
+
( typeStruct[ $art ]
|
|
1153
|
+
nullability[ $art ]?
|
|
1154
|
+
| typeArray[ $art ] // nullability is set in typeArray
|
|
1155
|
+
| typeTypeOf[ $art ]
|
|
1156
|
+
nullability[ $art ]?
|
|
1157
|
+
(
|
|
1158
|
+
ENUM '{' { $art.enum = this.createDict(); }
|
|
1159
|
+
enumSymbolDef[ $art ]*
|
|
1160
|
+
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1161
|
+
)?
|
|
1162
|
+
// TODO: no LOCALIZED ?
|
|
1163
|
+
| typeRefOptArgs[ $art ]
|
|
1164
|
+
nullability[ $art ]?
|
|
1165
|
+
(
|
|
1166
|
+
ENUM '{' { $art.enum = this.createDict(); }
|
|
1167
|
+
enumSymbolDef[ $art ]*
|
|
1168
|
+
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1169
|
+
)?
|
|
1054
1170
|
)
|
|
1055
1171
|
;
|
|
1056
1172
|
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
{ $art.location = this.startLocation();; this.docComment( $art ); }
|
|
1060
|
-
annotationAssignment_ll1[ $art ]*
|
|
1061
|
-
elementDefInner[ $art, $outer, false ]
|
|
1062
|
-
;
|
|
1063
|
-
|
|
1064
|
-
// Actually, this is a subset if elementDefInner...
|
|
1065
|
-
// TODO: the corresponding restrictions must also be checked in the core
|
|
1066
|
-
// compiler, as the mixin element could come via CSN
|
|
1067
|
-
mixinElementDef[ outer ] locals[ art = {} ]
|
|
1068
|
-
@after { /* #ATN 2 */ this.attachLocation($art); }
|
|
1069
|
-
:
|
|
1070
|
-
name=ident['Mixin']
|
|
1071
|
-
{ this.addDef( $art, $outer, 'mixin', 'mixin', $name.id ); }
|
|
1072
|
-
(
|
|
1073
|
-
':'
|
|
1074
|
-
// #ATN: referenced type name can be ASSOCIATION or COMPOSITION
|
|
1075
|
-
(
|
|
1076
|
-
typeAssociationBase[ $art, false ]
|
|
1077
|
-
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1078
|
-
( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
|
|
1079
|
-
typeAssociationCont[ $art ]?
|
|
1080
|
-
|
|
|
1081
|
-
typeRefOptArgs[ $art ]
|
|
1082
|
-
( as='=' expression
|
|
1083
|
-
{ this.error( 'syntax-unsupported-field', $as ); }
|
|
1084
|
-
)?
|
|
1085
|
-
)
|
|
1086
|
-
|
|
|
1087
|
-
as='=' expression
|
|
1088
|
-
{ this.error( 'syntax-unsupported-field', $as ); }
|
|
1089
|
-
)
|
|
1090
|
-
requiredSemi
|
|
1091
|
-
;
|
|
1092
|
-
|
|
1093
|
-
elementDefInner[ art, outer, allowEq ]
|
|
1094
|
-
@after{ /* #ATN 5 */ this.attachLocation( $art ); }
|
|
1095
|
-
:
|
|
1096
|
-
// TODO: it would be excellent to remove ELEMENT...
|
|
1097
|
-
// or have a special ident rule without the ELEMENT
|
|
1098
|
-
// Reason: it would be good for error recovery to start a major block without LL1 ambiguity
|
|
1099
|
-
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
1100
|
-
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[:{@=}]$/ ); }
|
|
1101
|
-
( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
|
|
1102
|
-
( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
|
|
1103
|
-
// #ATN: element name can be MASKED or ELEMENT (2x)
|
|
1104
|
-
( masked=MASKED
|
|
1105
|
-
{
|
|
1106
|
-
$art.masked = this.valueWithTokenLocation( true, $masked ) ;
|
|
1107
|
-
this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
|
|
1108
|
-
}
|
|
1109
|
-
)?
|
|
1110
|
-
// TODO: order?
|
|
1111
|
-
ELEMENT?
|
|
1112
|
-
name=ident['Element']
|
|
1113
|
-
{ this.addDef( $art, $outer, 'elements', 'element', $name.id );
|
|
1114
|
-
this.docComment( $art ); }
|
|
1115
|
-
annotationAssignment_fix[ $art ]*
|
|
1116
|
-
// TODO: we can think of making the typeSpec optional and do checks instead:
|
|
1117
|
-
// type optional with '=', type required otherwise
|
|
1118
|
-
(
|
|
1119
|
-
typeStruct[ $art ]
|
|
1120
|
-
( nullability[ $art ]
|
|
1121
|
-
requiredSemi
|
|
1122
|
-
| optionalSemi // NOT and NULL are reserved...
|
|
1123
|
-
)
|
|
1124
|
-
|
|
|
1125
|
-
':'
|
|
1126
|
-
// #ATN: referenced type name can be ASSOCIATION or ARRAY or TYPE or LOCALIZED
|
|
1127
|
-
(
|
|
1128
|
-
typeStruct[ $art ]
|
|
1129
|
-
nullability[ $art ]?
|
|
1130
|
-
requiredSemi
|
|
1131
|
-
|
|
|
1132
|
-
typeAssociationBase[ $art, true ]
|
|
1133
|
-
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1134
|
-
(
|
|
1135
|
-
typeStruct[ $art.target, true ] optionalSemi
|
|
1136
|
-
|
|
|
1137
|
-
one=ONE
|
|
1138
|
-
{ this.setMaxCardinality( $art, $one, this.numberLiteral( $one, null, '1' ) ); }
|
|
1139
|
-
typeCompoStruct[ $art.target ] optionalSemi
|
|
1140
|
-
|
|
|
1141
|
-
many=MANY
|
|
1142
|
-
{ this.setMaxCardinality( $art, $many, { literal: 'string', val: '*' } ); }
|
|
1143
|
-
typeCompoStruct[ $art.target ] optionalSemi
|
|
1144
|
-
|
|
|
1145
|
-
// we do not support `Composition of many { e }` - ambiguity ad-hoc target versus foreign keys!
|
|
1146
|
-
typeToMany[ $art ] typeAssociationElementCont[ $art ]
|
|
1147
|
-
|
|
|
1148
|
-
typeToOne[ $art ] typeAssociationElementCont[ $art ]
|
|
1149
|
-
|
|
|
1150
|
-
simplePath[ $art.target, 'artref' ] typeAssociationElementCont[ $art ]
|
|
1151
|
-
)
|
|
1152
|
-
|
|
|
1153
|
-
(
|
|
1154
|
-
array=ARRAY of=OF
|
|
1155
|
-
{ $art.items = { location: this.tokenLocation( $array, $of ) }; }
|
|
1156
|
-
| many=MANY
|
|
1157
|
-
{ $art.items = { location: this.tokenLocation( $many ) };}
|
|
1158
|
-
)
|
|
1159
|
-
// #ATN: typeRefOptArgs can start with TYPE
|
|
1160
|
-
( typeStruct[ $art.items ]
|
|
1161
|
-
nullability[ $art.items ]?
|
|
1162
|
-
| typeTypeOf[ $art.items ]
|
|
1163
|
-
nullability[ $art.items ]?
|
|
1164
|
-
{ this.docComment( $art ); }
|
|
1165
|
-
annotationAssignment_ll1[ $art ]*
|
|
1166
|
-
| typeRefOptArgs[ $art.items ]
|
|
1167
|
-
nullability[ $art.items ]? // only if not followed by `enum`
|
|
1168
|
-
{ this.docComment( $art ); }
|
|
1169
|
-
annotationAssignment_ll1[ $art ]*
|
|
1170
|
-
(
|
|
1171
|
-
{ if ($art.items.notNull) {
|
|
1172
|
-
this.message( 'syntax-unexpected-null', $art.items.notNull.location,
|
|
1173
|
-
{ keyword: $art.items.notNull.val ? 'not null' : 'null' } );
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
ENUM '{' { $art.items.enum = this.createDict(); }
|
|
1177
|
-
enumSymbolDef[ $art.items ]*
|
|
1178
|
-
'}' { this.finalizeDictOrArray( $art.items.enum ); }
|
|
1179
|
-
nullability[ $art.items ]?
|
|
1180
|
-
)?
|
|
1181
|
-
)
|
|
1182
|
-
requiredSemi // also req after struct/enum
|
|
1183
|
-
|
|
|
1184
|
-
typeTypeOf[ $art ] elementProperties[ $art ]?
|
|
1185
|
-
{ this.docComment( $art ); }
|
|
1186
|
-
annotationAssignment_ll1[ $art ]*
|
|
1187
|
-
requiredSemi // also req after foreign key spec
|
|
1188
|
-
|
|
|
1189
|
-
l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
1190
|
-
typeRefOptArgs[ $art ]
|
|
1191
|
-
{ this.docComment( $art ); }
|
|
1192
|
-
annotationAssignment_ll1[ $art ]*
|
|
1193
|
-
( elementProperties[ $art ]
|
|
1194
|
-
{ this.docComment( $art ); }
|
|
1195
|
-
annotationAssignment_ll1[ $art ]*
|
|
1196
|
-
)?
|
|
1197
|
-
requiredSemi
|
|
1198
|
-
|
|
|
1199
|
-
typeRefOptArgs[ $art ]
|
|
1200
|
-
{ this.docComment( $art ); }
|
|
1201
|
-
annotationAssignment_ll1[ $art ]*
|
|
1202
|
-
(
|
|
1203
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
1204
|
-
enumSymbolDef[ $art ]*
|
|
1205
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1206
|
-
elementProperties[ $art ]?
|
|
1207
|
-
|
|
|
1208
|
-
elementProperties[ $art ]
|
|
1209
|
-
{ this.docComment( $art ); }
|
|
1210
|
-
annotationAssignment_ll1[ $art ]*
|
|
1211
|
-
)?
|
|
1212
|
-
requiredSemi // also req after enum spec
|
|
1213
|
-
)
|
|
1214
|
-
|
|
|
1215
|
-
// this is also called for enum symbols (in EXTEND)
|
|
1216
|
-
eq='=' e=expression // never introduce AS as syntax variant of '='
|
|
1217
|
-
{
|
|
1218
|
-
if (!$allowEq || $e.expr && !$e.expr.literal )
|
|
1219
|
-
this.error( 'syntax-unsupported-field', $eq );
|
|
1220
|
-
else if ($e.expr)
|
|
1221
|
-
$art.value = $e.expr;
|
|
1222
|
-
}
|
|
1223
|
-
{ this.docComment( $art ); }
|
|
1224
|
-
annotationAssignment_ll1[ $art ]* // for enum symbol def via EXTEND
|
|
1225
|
-
requiredSemi
|
|
1226
|
-
)
|
|
1227
|
-
;
|
|
1228
|
-
|
|
1229
|
-
extendElement[ art, outer ]
|
|
1230
|
-
@after{ /* #ATN 1 */ this.attachLocation( $art ); }
|
|
1231
|
-
:
|
|
1232
|
-
// #ATN: element name can be ELEMENT
|
|
1233
|
-
( expected=ELEMENT { $art.expectedKind = 'element'; } )?
|
|
1234
|
-
name=ident['Element']
|
|
1235
|
-
{ this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
|
|
1236
|
-
extendWithOptElementsOrType[ $art, $art ]
|
|
1237
|
-
;
|
|
1238
|
-
|
|
1239
|
-
selectItemDef[ outer ] locals[ art ]
|
|
1240
|
-
@after{ if ($art) this.attachLocation( $art ); }
|
|
1241
|
-
:
|
|
1242
|
-
star='*'
|
|
1243
|
-
{ $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
|
|
1244
|
-
|
|
|
1245
|
-
{ $art = {};; this.docComment( $art ); }
|
|
1246
|
-
annotationAssignment_atn[ $art ]*
|
|
1247
|
-
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
1248
|
-
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^([,.:\[@]|as)$/i ) ; } // not '{'
|
|
1249
|
-
( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
|
|
1250
|
-
( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
|
|
1251
|
-
selectItemDefBody[ $art, $outer ]
|
|
1252
|
-
;
|
|
1253
|
-
|
|
1254
|
-
selectItemDefBody[ art, outer ]
|
|
1255
|
-
@after{ /* #ATN 2 */ }
|
|
1256
|
-
:
|
|
1257
|
-
{ $outer.push( $art ); }
|
|
1258
|
-
(
|
|
1259
|
-
e=expression { $art.value = $e.expr; }
|
|
1260
|
-
// we cannot use 'condition' instead, as long as we allow aliases without
|
|
1261
|
-
// AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
|
|
1262
|
-
( as=AS n1=ident['Item'] { $art.name = $n1.id }
|
|
1263
|
-
| n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
|
|
1264
|
-
| { if (this.getCurrentToken().text !== '.') this.classifyImplicitName( 'Item', $e.expr ); }
|
|
1265
|
-
)
|
|
1266
|
-
{ if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
|
|
1267
|
-
else if ($art.name) this.excludeExpected( ["'.'"] );
|
|
1268
|
-
}
|
|
1269
|
-
(
|
|
1270
|
-
{ this.reportExpandInline( $art, false ); }
|
|
1271
|
-
selectItemInlineList[ $art, 'expand' ]
|
|
1272
|
-
excludingClause[ $art ]?
|
|
1273
|
-
// TODO: we might alternatively allow AS here
|
|
1274
|
-
|
|
|
1275
|
-
{ this.reportExpandInline( $art, $as || this._input.LT(-1) ); }
|
|
1276
|
-
DOTbeforeBRACE // ...orASTERISK
|
|
1277
|
-
(
|
|
1278
|
-
selectItemInlineList[ $art, 'inline' ]
|
|
1279
|
-
excludingClause[ $art ]?
|
|
1280
|
-
|
|
|
1281
|
-
star='*'
|
|
1282
|
-
{ $art.inline = [ this.valueWithTokenLocation( '*', $star ) ]; }
|
|
1283
|
-
)
|
|
1284
|
-
)?
|
|
1285
|
-
|
|
|
1286
|
-
selectItemInlineList[ $art, 'expand' ]
|
|
1287
|
-
excludingClause[ $art ]?
|
|
1288
|
-
AS n1=ident['Item'] { $art.name = $n1.id }
|
|
1289
|
-
)
|
|
1290
|
-
{ this.docComment( $art ); }
|
|
1291
|
-
annotationAssignment_fix[ $art ]*
|
|
1292
|
-
( ':'
|
|
1293
|
-
// #ATN: typeRefOptArgs can start with TYPE, REDIRECTED
|
|
1294
|
-
( re=REDIRECTED to=TO
|
|
1295
|
-
{ $art.target = {}; }
|
|
1296
|
-
simplePath[ $art.target, 'artref' ]
|
|
1297
|
-
(
|
|
1298
|
-
typeAssociationCont[ $art ]
|
|
1299
|
-
|
|
|
1300
|
-
{ this.docComment( $art ); }
|
|
1301
|
-
annotationAssignment_ll1[ $art ]*
|
|
1302
|
-
)
|
|
1303
|
-
| typeTypeOf[ $art ]
|
|
1304
|
-
{ this.docComment( $art ); }
|
|
1305
|
-
annotationAssignment_ll1[ $art ]*
|
|
1306
|
-
| typeRefOptArgs[ $art ]
|
|
1307
|
-
{ this.docComment( $art ); }
|
|
1308
|
-
annotationAssignment_ll1[ $art ]*
|
|
1309
|
-
|
|
|
1310
|
-
typeAssociationBase[ $art, false ]
|
|
1311
|
-
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1312
|
-
( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
|
|
1313
|
-
typeAssociationCont[ $art ]?
|
|
1314
|
-
{ this.associationInSelectItem( $art ); }
|
|
1315
|
-
)
|
|
1316
|
-
)?
|
|
1317
|
-
;
|
|
1318
|
-
|
|
1319
|
-
bracedSelectItemListDef[ query ]
|
|
1320
|
-
:
|
|
1321
|
-
'{' { $query.columns = this.createArray(); }
|
|
1322
|
-
(
|
|
1323
|
-
selectItemDef[ $query.columns ]
|
|
1324
|
-
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1325
|
-
selectItemDef[ $query.columns ]
|
|
1326
|
-
)*
|
|
1327
|
-
)?
|
|
1328
|
-
'}' { this.finalizeDictOrArray( $query.columns ); }
|
|
1329
|
-
;
|
|
1330
|
-
|
|
1331
|
-
selectItemInlineList[ art, clause ]
|
|
1332
|
-
:
|
|
1333
|
-
'{' { $art[$clause] = this.createArray(); }
|
|
1334
|
-
(
|
|
1335
|
-
selectItemInlineDef[ $art[$clause] ]
|
|
1336
|
-
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1337
|
-
selectItemInlineDef[ $art[$clause] ]
|
|
1338
|
-
)*
|
|
1339
|
-
)?
|
|
1340
|
-
'}' { this.finalizeDictOrArray( $art[$clause] ); }
|
|
1341
|
-
;
|
|
1342
|
-
|
|
1343
|
-
selectItemInlineDef[ outer ] locals[ art ]
|
|
1344
|
-
@after{ if ($art) this.attachLocation( $art ); }
|
|
1345
|
-
:
|
|
1346
|
-
star='*'
|
|
1347
|
-
{ $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
|
|
1348
|
-
|
|
|
1349
|
-
{ $art = {};; this.docComment( $art ); }
|
|
1350
|
-
annotationAssignment_atn[ $art ]*
|
|
1351
|
-
selectItemDefBody[ $art, $outer ]
|
|
1352
|
-
;
|
|
1353
|
-
|
|
1354
|
-
parameterListDef[ art ]
|
|
1355
|
-
:
|
|
1356
|
-
'(' { $art.params = this.createDict(); }
|
|
1357
|
-
// also empty param list (we might do some hacking later to allow reserved words)
|
|
1358
|
-
// see annotationAssignment_paren
|
|
1359
|
-
(
|
|
1360
|
-
parameterDef[ $art ]
|
|
1361
|
-
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1362
|
-
parameterDef[ $art ]
|
|
1363
|
-
)*
|
|
1364
|
-
)?
|
|
1365
|
-
')' { this.finalizeDictOrArray( $art.params ); }
|
|
1366
|
-
;
|
|
1367
|
-
|
|
1368
|
-
parameterDef[ outer ] locals[ art = {} ]
|
|
1369
|
-
@after { this.attachLocation( $art ); }
|
|
1370
|
-
:
|
|
1371
|
-
{ this.docComment( $art ); }
|
|
1372
|
-
annotationAssignment_ll1[ $art ]*
|
|
1373
|
-
name=ident['Param']
|
|
1374
|
-
{ this.addDef( $art, $outer, 'params', 'param', $name.id );
|
|
1375
|
-
this.docComment( $art ); }
|
|
1376
|
-
annotationAssignment_fix[ $art ]*
|
|
1377
|
-
typeSpec[ $art ]
|
|
1378
|
-
defaultValue[ $art ]?
|
|
1379
|
-
{ this.docComment( $art ); }
|
|
1380
|
-
annotationAssignment_ll1[ $art ]*
|
|
1381
|
-
;
|
|
1382
|
-
|
|
1383
|
-
nullability[ art ]
|
|
1384
|
-
:
|
|
1385
|
-
not=NOT n1=NULL
|
|
1386
|
-
{ $art.notNull = this.valueWithTokenLocation( true, $not, $n1 ); }
|
|
1387
|
-
|
|
|
1388
|
-
n2=NULL
|
|
1389
|
-
{ $art.notNull = this.valueWithTokenLocation( false, $n2 ); }
|
|
1390
|
-
;
|
|
1391
|
-
|
|
1392
|
-
elementProperties[ elem ]
|
|
1393
|
-
:
|
|
1394
|
-
nullability[ $elem ]
|
|
1395
|
-
defaultValue[ $elem ]?
|
|
1396
|
-
|
|
|
1397
|
-
defaultValue[ $elem ]
|
|
1398
|
-
nullability[ $elem ]?
|
|
1399
|
-
|
|
|
1400
|
-
eq='='
|
|
1401
|
-
{ this.error( 'syntax-unsupported-field', $eq ); }
|
|
1402
|
-
expression
|
|
1403
|
-
;
|
|
1404
|
-
|
|
1405
|
-
// View definitions ----------------------------------------------------------
|
|
1406
|
-
|
|
1407
|
-
viewDef[ art, outer ] locals[ name = {} ]
|
|
1408
|
-
@after { this.attachLocation( $art ); }
|
|
1409
|
-
:
|
|
1410
|
-
v=VIEW simplePath[ $name, 'Entity' ]
|
|
1411
|
-
{ $art['$'+'syntax'] = 'view';
|
|
1412
|
-
this.addDef( $art, $outer, 'artifacts', 'entity', $name );
|
|
1413
|
-
this.docComment( $art ); }
|
|
1414
|
-
annotationAssignment_fix[ $art ]*
|
|
1415
|
-
(
|
|
1416
|
-
parameterListDef[ $art ]
|
|
1417
|
-
|
|
|
1418
|
-
// TODO: warning deprecated?
|
|
1419
|
-
( HideAlternatives | WITH ) { $art.params = this.createDict(); }
|
|
1420
|
-
PARAMETERS
|
|
1421
|
-
parameterDef[ $art ]
|
|
1422
|
-
( ',' parameterDef[ $art ] )* // no optional final ',' here
|
|
1423
|
-
{ this.finalizeDictOrArray( $art.params ); }
|
|
1424
|
-
)?
|
|
1425
|
-
AS qe=queryExpression { $art.query = $qe.query; }
|
|
1426
|
-
// TODO check ANTLR: bad msg with 'view V as'<eof> but 'view V as FOO' is fine
|
|
1427
|
-
requiredSemi
|
|
1428
|
-
;
|
|
1429
|
-
|
|
1430
|
-
// Type references -----------------------------------------------------------
|
|
1431
|
-
|
|
1432
|
-
includeRef[ art ] locals[ incl = {} ]
|
|
1433
|
-
:
|
|
1434
|
-
simplePath[ $incl, 'artref' ]
|
|
1435
|
-
{ if ($art.includes) $art.includes.push($incl); else $art.includes = [$incl]; }
|
|
1436
|
-
;
|
|
1437
|
-
|
|
1438
|
-
typeSpec[ art ] // for params
|
|
1439
|
-
@after{ /* #ATN 1 */ }
|
|
1440
|
-
:
|
|
1441
|
-
typeStruct[ $art ]
|
|
1442
|
-
|
|
|
1443
|
-
':'
|
|
1444
|
-
// #ATN: typeSimple can start with ARRAY or TYPE
|
|
1445
|
-
( typeStruct[ $art ]
|
|
1446
|
-
nullability[ $art ]?
|
|
1447
|
-
| typeArray[ $art ] // nullability is set in typeArray
|
|
1448
|
-
| typeTypeOf[ $art ]
|
|
1449
|
-
nullability[ $art ]?
|
|
1450
|
-
// TODO: no LOCALIZED ?
|
|
1451
|
-
| typeRefOptArgs[ $art ]
|
|
1452
|
-
nullability[ $art ]?
|
|
1453
|
-
(
|
|
1454
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
1455
|
-
enumSymbolDef[ $art ]*
|
|
1456
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1457
|
-
)?
|
|
1458
|
-
)
|
|
1459
|
-
;
|
|
1460
|
-
|
|
1461
|
-
returnTypeSpec[ art ]
|
|
1462
|
-
@after{ /* #ATN 1 */ }
|
|
1173
|
+
returnTypeSpec[ art ]
|
|
1174
|
+
@after{ /* #ATN 1 */ }
|
|
1463
1175
|
:
|
|
1464
1176
|
ret=RETURNS { $art.returns = { location: this.tokenLocation( $ret ), kind: 'param' }; }
|
|
1465
1177
|
// #ATN: typeSimple can start with ARRAY or TYPE
|
|
@@ -1468,6 +1180,11 @@ returnTypeSpec[ art ]
|
|
|
1468
1180
|
| typeArray[ $art.returns ] // nullability is set in typeArray
|
|
1469
1181
|
| typeTypeOf[ $art.returns ]
|
|
1470
1182
|
nullability[ $art.returns ]?
|
|
1183
|
+
(
|
|
1184
|
+
ENUM '{' { $art.returns.enum = this.createDict(); }
|
|
1185
|
+
enumSymbolDef[ $art.returns ]*
|
|
1186
|
+
'}' { this.finalizeDictOrArray( $art.returns.enum ); }
|
|
1187
|
+
)?
|
|
1471
1188
|
// TODO: no LOCALIZED ?
|
|
1472
1189
|
| typeRefOptArgs[ $art.returns ]
|
|
1473
1190
|
nullability[ $art.returns ]?
|
|
@@ -1627,6 +1344,11 @@ typeArray[ art ]
|
|
|
1627
1344
|
nullability[ $art.items ]?
|
|
1628
1345
|
| typeTypeOf[ $art.items ]
|
|
1629
1346
|
nullability[ $art.items ]?
|
|
1347
|
+
(
|
|
1348
|
+
ENUM '{' { $art.items.enum = this.createDict(); }
|
|
1349
|
+
enumSymbolDef[ $art.items ]*
|
|
1350
|
+
'}' { this.finalizeDictOrArray( $art.items.enum ); }
|
|
1351
|
+
)?
|
|
1630
1352
|
| typeRefOptArgs[ $art.items ]
|
|
1631
1353
|
nullability[ $art.items ]?
|
|
1632
1354
|
(
|
|
@@ -1742,235 +1464,499 @@ cardinality[ art ] locals[ card = {} ]
|
|
|
1742
1464
|
location: this.tokenLocation($trgMaxStar) }; }
|
|
1743
1465
|
)
|
|
1744
1466
|
)?
|
|
1745
|
-
']'
|
|
1746
|
-
;
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1467
|
+
']'
|
|
1468
|
+
;
|
|
1469
|
+
|
|
1470
|
+
nullability[ art ]
|
|
1471
|
+
:
|
|
1472
|
+
not=NOT n1=NULL
|
|
1473
|
+
{ $art.notNull = this.valueWithTokenLocation( true, $not, $n1 ); }
|
|
1474
|
+
|
|
|
1475
|
+
n2=NULL
|
|
1476
|
+
{ $art.notNull = this.valueWithTokenLocation( false, $n2 ); }
|
|
1477
|
+
;
|
|
1478
|
+
|
|
1479
|
+
foreignKey[ outer ] locals[ art = {}, elem = {} ]
|
|
1480
|
+
@after { this.attachLocation($art); }
|
|
1481
|
+
:
|
|
1482
|
+
simplePath[ $elem, 'ref' ] { $art.targetElement = $elem; }
|
|
1483
|
+
( AS name=ident['Key'] )?
|
|
1484
|
+
{ this.addDef( $art, $outer, 'foreignKeys', 'key', ($ctx.name) ? $name.id : $elem.path ); }
|
|
1485
|
+
;
|
|
1486
|
+
|
|
1487
|
+
typeTypeOf[ art ] locals[ _sync = 'nop' ]
|
|
1488
|
+
@after { this.attachLocation($art.type); }
|
|
1489
|
+
:
|
|
1490
|
+
TYPE OF
|
|
1491
|
+
{ $art.type = { scope: 'typeOf' }; }
|
|
1492
|
+
simplePath[ $art.type, 'ref' ]
|
|
1493
|
+
( ':'
|
|
1494
|
+
// If we have too much time, we could set the category of the simple path
|
|
1495
|
+
// before to 'artref'
|
|
1496
|
+
{ $art.type.scope = $art.type.path.length; }
|
|
1497
|
+
simplePath[ $art.type, 'ref']
|
|
1498
|
+
)?
|
|
1499
|
+
;
|
|
1500
|
+
|
|
1501
|
+
typeRefOptArgs[ art ]
|
|
1502
|
+
@init { $art.type = {}; }
|
|
1503
|
+
:
|
|
1504
|
+
simplePath[ $art.type, 'artref' ]
|
|
1505
|
+
(
|
|
1506
|
+
typeRefArgs[ $art ]
|
|
1507
|
+
|
|
|
1508
|
+
':'
|
|
1509
|
+
{ $art.type.scope = $art.type.path.length; }
|
|
1510
|
+
simplePath[ $art.type, 'ref']
|
|
1511
|
+
)?
|
|
1512
|
+
;
|
|
1513
|
+
|
|
1514
|
+
typeRefArgs[ art ]
|
|
1515
|
+
:
|
|
1516
|
+
paren='(' { $art['$'+'typeArgs'] = this.createArray(); }
|
|
1517
|
+
(
|
|
1518
|
+
// unnamed arguments
|
|
1519
|
+
head=Number
|
|
1520
|
+
{ $art['$'+'typeArgs'].push( this.numberLiteral( $head ) ); }
|
|
1521
|
+
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1522
|
+
(
|
|
1523
|
+
v=VARIABLE
|
|
1524
|
+
{ $art['$'+'typeArgs'].push(
|
|
1525
|
+
{ literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
|
|
1526
|
+
}
|
|
1527
|
+
|
|
|
1528
|
+
f=FLOATING
|
|
1529
|
+
{ $art['$'+'typeArgs'].push(
|
|
1530
|
+
{ literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
|
|
1531
|
+
}
|
|
1532
|
+
|
|
|
1533
|
+
tail=Number
|
|
1534
|
+
{ $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
|
|
1535
|
+
)
|
|
1536
|
+
)*
|
|
1537
|
+
|
|
|
1538
|
+
// named arguments
|
|
1539
|
+
typeNamedArg[ $art ]
|
|
1540
|
+
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1541
|
+
typeNamedArg[ $art ]
|
|
1542
|
+
)*
|
|
1543
|
+
)
|
|
1544
|
+
')'{ this.finalizeDictOrArray( $art['$'+'typeArgs']); }
|
|
1545
|
+
;
|
|
1546
|
+
|
|
1547
|
+
typeNamedArgList[ art ]
|
|
1548
|
+
:
|
|
1549
|
+
paren='('
|
|
1550
|
+
typeNamedArg[ $art ]
|
|
1551
|
+
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1552
|
+
typeNamedArg[ $art ]
|
|
1553
|
+
)*
|
|
1554
|
+
')'
|
|
1555
|
+
;
|
|
1556
|
+
|
|
1557
|
+
typeNamedArg[ art ] locals[ arg = '' ]
|
|
1558
|
+
:
|
|
1559
|
+
name=ident['paramname']
|
|
1560
|
+
':'
|
|
1561
|
+
{ if ($name.id && this.checkTypeFacet( $art, $name.id ))
|
|
1562
|
+
$arg = $name.id.id;
|
|
1563
|
+
}
|
|
1564
|
+
(
|
|
1565
|
+
val=Number
|
|
1566
|
+
{ if ($arg && $art && $name.id) {
|
|
1567
|
+
$art[$arg] = this.numberLiteral( $val );
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
|
|
|
1571
|
+
v=VARIABLE
|
|
1572
|
+
{ if ($arg && $art && $name.id) {
|
|
1573
|
+
$art[$arg] = { literal: 'string', val: 'variable', location: this.tokenLocation($v) };
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
|
1577
|
+
f=FLOATING
|
|
1578
|
+
{ if ($arg && $art && $name.id) {
|
|
1579
|
+
$art[$arg] = { literal: 'string', val: 'floating', location: this.tokenLocation($f) };
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
)
|
|
1583
|
+
;
|
|
1584
|
+
|
|
1585
|
+
// Queries: the main query structure --------------------------------------------
|
|
1586
|
+
|
|
1587
|
+
queryEOF returns [ query ]
|
|
1588
|
+
:
|
|
1589
|
+
q=queryExpression { $query = $q.query; } EOF
|
|
1590
|
+
;
|
|
1591
|
+
|
|
1592
|
+
projectionSpec returns[ query ] locals[ src ]
|
|
1593
|
+
@after { this.attachLocation($query); }
|
|
1594
|
+
:
|
|
1595
|
+
proj=PROJECTION ON
|
|
1596
|
+
// now a simplified `tableTerm`:
|
|
1597
|
+
{
|
|
1598
|
+
$src = { path: [], scope: 0 };
|
|
1599
|
+
$query = { op: this.valueWithTokenLocation( 'SELECT', $proj ), from: $src, location: this.startLocation() };
|
|
1600
|
+
}
|
|
1601
|
+
fromPath[ $src, 'artref']
|
|
1602
|
+
( ':'
|
|
1603
|
+
{ $src.scope = $src.path.length; }
|
|
1604
|
+
fromPath[ $src, 'ref']
|
|
1605
|
+
)?
|
|
1606
|
+
( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
|
|
1607
|
+
// ANTLR errors are better if we use ( A )? instead of ( A | ):
|
|
1608
|
+
{ if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
|
|
1609
|
+
bracedSelectItemListDef[ $query, 'columns' ]?
|
|
1610
|
+
excludingClause[ $query ]?
|
|
1611
|
+
;
|
|
1612
|
+
|
|
1613
|
+
projectionClauses[ query ]
|
|
1614
|
+
@after { this.attachLocation($query); }
|
|
1615
|
+
:
|
|
1616
|
+
( WHERE cond=condition { $query.where = $cond.cond; } )?
|
|
1617
|
+
(
|
|
1618
|
+
GROUP BY
|
|
1619
|
+
e1=expression { $query.groupBy = [ $e1.expr ]; }
|
|
1620
|
+
( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
|
|
1621
|
+
)?
|
|
1622
|
+
( HAVING having=condition { $query.having = $having.cond; } )?
|
|
1623
|
+
( ob=orderByClause[ $query ] { $query = $ob.query; } ) ?
|
|
1624
|
+
( lc=limitClause[ $query ] { $query = $lc.query; } ) ?
|
|
1625
|
+
;
|
|
1626
|
+
|
|
1627
|
+
queryExpression returns[ query ] // QLSubqueryComplex, SubqueryComplex
|
|
1628
|
+
@after{ this.attachLocation($query); }
|
|
1629
|
+
:
|
|
1630
|
+
qt1=queryTerm { $query = $qt1.query; }
|
|
1631
|
+
(
|
|
1632
|
+
( op=UNION q=( DISTINCT | ALL )?
|
|
1633
|
+
| op=EXCEPT q=DISTINCT?
|
|
1634
|
+
| op=MINUS q=DISTINCT?
|
|
1635
|
+
)
|
|
1636
|
+
qt=queryTerm
|
|
1637
|
+
{ if ($qt.query) $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
|
|
1638
|
+
)*
|
|
1639
|
+
( ob=orderByClause[ $query ] { if ($ob.query) $query = $ob.query; } ) ?
|
|
1640
|
+
( lc=limitClause[ $query ] { if ($lc.query) $query = $lc.query; } ) ?
|
|
1641
|
+
;
|
|
1642
|
+
|
|
1643
|
+
queryTerm returns[ query ]
|
|
1644
|
+
@after{ this.attachLocation($query); }
|
|
1645
|
+
:
|
|
1646
|
+
qt1=queryPrimary { $query = $qt1.query; }
|
|
1647
|
+
(
|
|
1648
|
+
intersect=INTERSECT quantifier=DISTINCT?
|
|
1649
|
+
qt=queryPrimary
|
|
1650
|
+
{ $query = this.leftAssocBinaryOp( $query, $intersect, $quantifier, $qt.query );
|
|
1651
|
+
$ctx.quantifier = null; } // reset for loop
|
|
1652
|
+
)*
|
|
1653
|
+
;
|
|
1654
|
+
|
|
1655
|
+
queryPrimary returns[ query = {} ]
|
|
1656
|
+
@after { this.attachLocation($query); }
|
|
1657
|
+
:
|
|
1658
|
+
open='(' qe=queryExpression close=')'
|
|
1659
|
+
{ $query = this.surroundByParens( $qe.query, $open, $close ); }
|
|
1660
|
+
|
|
|
1661
|
+
select=SELECT
|
|
1662
|
+
{ $query = { op: this.valueWithTokenLocation( 'SELECT', $select ), location: this.startLocation() }; }
|
|
1663
|
+
(
|
|
1664
|
+
FROM querySource[ $query ]
|
|
1665
|
+
(
|
|
1666
|
+
mixin=MIXIN '{' { $query.mixin = this.createDict(); }
|
|
1667
|
+
mixinElementDef[ $query ]*
|
|
1668
|
+
'}' { this.finalizeDictOrArray( $query.mixin ); }
|
|
1669
|
+
INTO
|
|
1670
|
+
)?
|
|
1671
|
+
( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
|
|
1672
|
+
{ $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
|
|
1673
|
+
)?
|
|
1674
|
+
bracedSelectItemListDef[ $query, 'columns' ]?
|
|
1675
|
+
excludingClause[ $query ]?
|
|
1676
|
+
|
|
|
1677
|
+
( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
|
|
1678
|
+
{ $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
|
|
1679
|
+
)?
|
|
1680
|
+
{ $query.columns = []; } // set it early to avoid "wildcard" errors
|
|
1681
|
+
selectItemDef[ $query.columns ]
|
|
1682
|
+
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1683
|
+
selectItemDef[ $query.columns ]
|
|
1684
|
+
)*
|
|
1685
|
+
FROM querySource[ $query ]
|
|
1686
|
+
)
|
|
1687
|
+
( WHERE cond=condition { $query.where = $cond.cond; } )?
|
|
1688
|
+
(
|
|
1689
|
+
GROUP BY
|
|
1690
|
+
e1=expression { $query.groupBy = [ $e1.expr ]; }
|
|
1691
|
+
( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
|
|
1692
|
+
)?
|
|
1693
|
+
( HAVING having=condition { $query.having = $having.cond; } )?
|
|
1754
1694
|
;
|
|
1755
1695
|
|
|
1756
|
-
|
|
1757
|
-
@after { this.attachLocation($
|
|
1696
|
+
querySource[ query ]
|
|
1697
|
+
@after { this.attachLocation($query.from); }
|
|
1758
1698
|
:
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
{ $
|
|
1766
|
-
simplePath[ $art.type, 'ref']
|
|
1699
|
+
t1=tableExpression { $query.from = $t1.table; }
|
|
1700
|
+
(
|
|
1701
|
+
{ const location = this.tokenLocation( this.getCurrentToken() );
|
|
1702
|
+
$query.from = { op: { val: 'join', location },
|
|
1703
|
+
join: { val: 'cross', location },
|
|
1704
|
+
args: [$t1.table] }; }
|
|
1705
|
+
( ',' tn=tableExpression { if ($tn.table) $query.from.args.push( $tn.table ); } )+
|
|
1767
1706
|
)?
|
|
1768
1707
|
;
|
|
1769
1708
|
|
|
1770
|
-
|
|
1771
|
-
@
|
|
1709
|
+
tableExpression returns[ table ] // TableOrJoin
|
|
1710
|
+
@after { this.attachLocation($table); }
|
|
1772
1711
|
:
|
|
1773
|
-
|
|
1712
|
+
qt=tableTerm { $table = $qt.table; }
|
|
1774
1713
|
(
|
|
1775
|
-
|
|
1714
|
+
join=joinOp[ $table ] { $table = $join.table; }
|
|
1715
|
+
te=tableExpression
|
|
1716
|
+
{ if (!$table) { $table = {}; } else if ($te.table) $table.args.push( $te.table ); }
|
|
1717
|
+
ON cond=condition { $table.on = $cond.cond; }
|
|
1776
1718
|
|
|
|
1777
|
-
|
|
1778
|
-
{ $
|
|
1779
|
-
|
|
1780
|
-
)?
|
|
1719
|
+
crj=CROSS jn=JOIN tt=tableTerm
|
|
1720
|
+
{ if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
|
|
1721
|
+
)*
|
|
1781
1722
|
;
|
|
1782
1723
|
|
|
1783
|
-
|
|
1724
|
+
tableTerm returns [ table ]
|
|
1725
|
+
@after{ /* #ATN 1 */ this.attachLocation($table); }
|
|
1784
1726
|
:
|
|
1785
|
-
|
|
1727
|
+
{ $table = { path: [], scope: 0 }; }
|
|
1728
|
+
fromPath[ $table, 'artref']
|
|
1729
|
+
( ':'
|
|
1730
|
+
{ $table.scope = $table.path.length; }
|
|
1731
|
+
fromPath[ $table, 'ref']
|
|
1732
|
+
)?
|
|
1733
|
+
( AS n1=ident['FromAlias'] { $table.name = $n1.id }
|
|
1734
|
+
| n2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $n2.id ); }
|
|
1735
|
+
// if we would use rule `ident`, we would either had to make all JOIN
|
|
1736
|
+
// kinds reserved or introduce ATN
|
|
1737
|
+
)?
|
|
1738
|
+
// ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
|
|
1739
|
+
{ if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
|
|
1740
|
+
|
|
|
1741
|
+
open='('
|
|
1742
|
+
// #ATN: The following alternative is not LL1, because both can start with
|
|
1743
|
+
// left-paren, but queryExpression has SELECT after initial left-parens
|
|
1786
1744
|
(
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
{ $art['$'+'typeArgs'].push(
|
|
1794
|
-
{ literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
|
|
1795
|
-
}
|
|
1796
|
-
|
|
|
1797
|
-
f=FLOATING
|
|
1798
|
-
{ $art['$'+'typeArgs'].push(
|
|
1799
|
-
{ literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
|
|
1800
|
-
}
|
|
1801
|
-
|
|
|
1802
|
-
tail=Number
|
|
1803
|
-
{ $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
|
|
1804
|
-
)
|
|
1805
|
-
)*
|
|
1745
|
+
qe=queryExpression close=')'
|
|
1746
|
+
{ $table = this.surroundByParens( $qe.query, $open, $close, true ); }
|
|
1747
|
+
( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table aliass
|
|
1748
|
+
| a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
|
|
1749
|
+
// not using ident` to have a similar behavior to above
|
|
1750
|
+
)
|
|
1806
1751
|
|
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1810
|
-
typeNamedArg[ $art ]
|
|
1811
|
-
)*
|
|
1752
|
+
te=tableExpression close=')'
|
|
1753
|
+
{ $table = this.surroundByParens( $te.table, $open, $close ); }
|
|
1812
1754
|
)
|
|
1813
|
-
')'{ this.finalizeDictOrArray( $art['$'+'typeArgs']); }
|
|
1814
1755
|
;
|
|
1815
1756
|
|
|
1816
|
-
|
|
1757
|
+
fromPath[ qp, idkind ]
|
|
1758
|
+
@after{ this.attachLocation($qp); }
|
|
1817
1759
|
:
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1760
|
+
id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
1761
|
+
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
1762
|
+
| cardinalityAndFilter[ $id.id ]
|
|
1763
|
+
)?
|
|
1764
|
+
(
|
|
1765
|
+
'.' id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
1766
|
+
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
1767
|
+
| cardinalityAndFilter[ $id.id ]
|
|
1768
|
+
)?
|
|
1822
1769
|
)*
|
|
1823
|
-
')'
|
|
1824
1770
|
;
|
|
1825
1771
|
|
|
1826
|
-
|
|
1772
|
+
fromArguments[ pathStep ]
|
|
1773
|
+
@init{ if (!$pathStep) $pathStep = {}; } // grammar robustness, see test/negative/parser/NamedExpression.cds
|
|
1827
1774
|
:
|
|
1828
|
-
|
|
1829
|
-
':'
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
$art[$arg] = this.numberLiteral( $val );
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
|
|
|
1840
|
-
v=VARIABLE
|
|
1841
|
-
{ if ($arg && $art && $name.id) {
|
|
1842
|
-
$art[$arg] = { literal: 'string', val: 'variable', location: this.tokenLocation($v) };
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
|
|
|
1846
|
-
f=FLOATING
|
|
1847
|
-
{ if ($arg && $art && $name.id) {
|
|
1848
|
-
$art[$arg] = { literal: 'string', val: 'floating', location: this.tokenLocation($f) };
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
)
|
|
1775
|
+
'(' { $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; } // necessary?
|
|
1776
|
+
name=ident['paramname'] ':'
|
|
1777
|
+
namedExpression[ $pathStep, $name.id ]
|
|
1778
|
+
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1779
|
+
name=ident['paramname'] ':'
|
|
1780
|
+
namedExpression[ $pathStep, $name.id ]
|
|
1781
|
+
)*
|
|
1782
|
+
')' { this.finalizeDictOrArray( $pathStep.args ); }
|
|
1852
1783
|
;
|
|
1853
1784
|
|
|
1785
|
+
// Queries: columns and other clauses -------------------------------------------
|
|
1854
1786
|
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
queryExpression returns[ query ] // QLSubqueryComplex, SubqueryComplex
|
|
1858
|
-
@after{ this.attachLocation($query); }
|
|
1787
|
+
excludingClause[ query ]
|
|
1859
1788
|
:
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
qt=queryTerm
|
|
1867
|
-
{ if ($qt.query) $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
|
|
1789
|
+
// syntax is less than ideal - EXCLUDING is only useful for `*` - with
|
|
1790
|
+
// this syntax, people wonder what happens with explicit select items
|
|
1791
|
+
EXCLUDING '{' { $query.excludingDict = this.createDict(); }
|
|
1792
|
+
projectionExclusion[ $query ]
|
|
1793
|
+
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1794
|
+
projectionExclusion[ $query ]
|
|
1868
1795
|
)*
|
|
1869
|
-
|
|
1870
|
-
( lc=limitClause[ $query ] { if ($lc.query) $query = $lc.query; } ) ?
|
|
1871
|
-
;
|
|
1872
|
-
|
|
1873
|
-
orderByClause[ inQuery ] returns [ query ]
|
|
1874
|
-
:
|
|
1875
|
-
ORDER BY { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
|
|
1876
|
-
ob1=orderBySpec { $query.orderBy = [ $ob1.ob ]; }
|
|
1877
|
-
( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
|
|
1796
|
+
'}' { this.finalizeDictOrArray( $query.excludingDict ); }
|
|
1878
1797
|
;
|
|
1879
1798
|
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
functionOrderByClause[ lhsExpr ] returns [ expr ]
|
|
1883
|
-
@after { this.attachLocation( $expr ); }
|
|
1799
|
+
projectionExclusion[ outer ] locals[ art = {} ]
|
|
1800
|
+
@after { this.attachLocation($art); }
|
|
1884
1801
|
:
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
|
|
1802
|
+
name=ident['ref']
|
|
1803
|
+
{ this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
|
|
1888
1804
|
;
|
|
1889
1805
|
|
|
1890
|
-
|
|
1891
|
-
|
|
1806
|
+
// Actually, this is a subset if elementDefInner...
|
|
1807
|
+
// TODO: the corresponding restrictions must also be checked in the core
|
|
1808
|
+
// compiler, as the mixin element could come via CSN
|
|
1809
|
+
mixinElementDef[ outer ] locals[ art = {} ]
|
|
1810
|
+
@after { /* #ATN 2 */ this.attachLocation($art); }
|
|
1892
1811
|
:
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
(
|
|
1812
|
+
name=ident['Mixin']
|
|
1813
|
+
{ this.addDef( $art, $outer, 'mixin', 'mixin', $name.id ); }
|
|
1814
|
+
(
|
|
1815
|
+
':'
|
|
1816
|
+
// #ATN: referenced type name can be ASSOCIATION or COMPOSITION
|
|
1817
|
+
(
|
|
1818
|
+
typeAssociationBase[ $art, false ]
|
|
1819
|
+
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1820
|
+
( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
|
|
1821
|
+
typeAssociationCont[ $art ]?
|
|
1822
|
+
|
|
|
1823
|
+
typeRefOptArgs[ $art ]
|
|
1824
|
+
( as='=' expression
|
|
1825
|
+
{ this.error( 'syntax-unsupported-calc-elem', $as ); }
|
|
1826
|
+
)?
|
|
1827
|
+
)
|
|
1828
|
+
|
|
|
1829
|
+
as='=' expression
|
|
1830
|
+
{ this.error( 'syntax-unsupported-calc-elem', $as ); }
|
|
1831
|
+
)
|
|
1832
|
+
requiredSemi
|
|
1896
1833
|
;
|
|
1897
1834
|
|
|
1898
|
-
|
|
1899
|
-
@after
|
|
1835
|
+
selectItemDef[ outer ] locals[ art ]
|
|
1836
|
+
@after{ if ($art) this.attachLocation( $art ); }
|
|
1900
1837
|
:
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1838
|
+
star='*'
|
|
1839
|
+
{ $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
|
|
1840
|
+
|
|
|
1841
|
+
{ $art = {};; this.docComment( $art ); }
|
|
1842
|
+
annotationAssignment_atn[ $art ]*
|
|
1843
|
+
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
1844
|
+
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^([,.:\[@]|as)$/i ) ; } // not '{'
|
|
1845
|
+
( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
|
|
1846
|
+
( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
|
|
1847
|
+
selectItemDefBody[ $art, $outer ]
|
|
1904
1848
|
;
|
|
1905
1849
|
|
|
1906
|
-
|
|
1907
|
-
@after
|
|
1850
|
+
selectItemDefBody[ art, outer ] locals[ assoc ]
|
|
1851
|
+
@after{ /* #ATN 2 */ }
|
|
1908
1852
|
:
|
|
1909
|
-
|
|
1910
|
-
|
|
1853
|
+
{ $outer.push( $art ); }
|
|
1854
|
+
(
|
|
1855
|
+
e=expression { $art.value = $e.expr; }
|
|
1856
|
+
// we cannot use 'condition' instead, as long as we allow aliases without
|
|
1857
|
+
// AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
|
|
1858
|
+
( as=AS n1=ident['Item'] { $art.name = $n1.id }
|
|
1859
|
+
| n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
|
|
1860
|
+
| { this.classifyImplicitName( 'Item', $e.expr ); }
|
|
1861
|
+
)
|
|
1862
|
+
{ if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
|
|
1863
|
+
else if ($art.name) this.excludeExpected( ["'.'"] );
|
|
1864
|
+
}
|
|
1865
|
+
(
|
|
1866
|
+
{ this.reportExpandInline( $art, false ); }
|
|
1867
|
+
selectItemInlineList[ $art, 'expand' ]
|
|
1868
|
+
excludingClause[ $art ]?
|
|
1869
|
+
// TODO: we might alternatively allow AS here
|
|
1870
|
+
|
|
|
1871
|
+
{ this.reportExpandInline( $art, $as || this._input.LT(-1) ); }
|
|
1872
|
+
DOTbeforeBRACE // ...orASTERISK
|
|
1873
|
+
(
|
|
1874
|
+
selectItemInlineList[ $art, 'inline' ]
|
|
1875
|
+
excludingClause[ $art ]?
|
|
1876
|
+
|
|
|
1877
|
+
star='*'
|
|
1878
|
+
{ $art.inline = [ this.valueWithTokenLocation( '*', $star ) ]; }
|
|
1879
|
+
)
|
|
1880
|
+
)?
|
|
1881
|
+
|
|
|
1882
|
+
selectItemInlineList[ $art, 'expand' ]
|
|
1883
|
+
excludingClause[ $art ]?
|
|
1884
|
+
AS n1=ident['Item'] { $art.name = $n1.id }
|
|
1885
|
+
)
|
|
1886
|
+
{ this.docComment( $art ); }
|
|
1887
|
+
annotationAssignment_fix[ $art ]*
|
|
1888
|
+
( ':'
|
|
1889
|
+
// #ATN: typeRefOptArgs can start with TYPE, REDIRECTED, ASSOCIATION
|
|
1890
|
+
( re=REDIRECTED to=TO
|
|
1891
|
+
{ $art.target = {}; }
|
|
1892
|
+
simplePath[ $art.target, 'artref' ]
|
|
1893
|
+
(
|
|
1894
|
+
typeAssociationCont[ $art ]
|
|
1895
|
+
|
|
|
1896
|
+
{ this.docComment( $art ); }
|
|
1897
|
+
annotationAssignment_ll1[ $art ]*
|
|
1898
|
+
)
|
|
1899
|
+
| typeTypeOf[ $art ]
|
|
1900
|
+
{ this.docComment( $art ); }
|
|
1901
|
+
annotationAssignment_ll1[ $art ]*
|
|
1902
|
+
| l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
1903
|
+
typeRefOptArgs[ $art ]
|
|
1904
|
+
{ this.docComment( $art ); }
|
|
1905
|
+
annotationAssignment_ll1[ $art ]*
|
|
1906
|
+
| typeRefOptArgs[ $art ]
|
|
1907
|
+
{ this.docComment( $art ); }
|
|
1908
|
+
annotationAssignment_ll1[ $art ]*
|
|
1909
|
+
|
|
|
1910
|
+
{ $assoc = this.associationInSelectItem( $art ); }
|
|
1911
|
+
typeAssociationBase[ $assoc, false ]
|
|
1912
|
+
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1913
|
+
( typeToMany[ $assoc ] | typeToOne[ $assoc ] | simplePath[ $assoc.target, 'artref' ] )
|
|
1914
|
+
ON cond=condition
|
|
1915
|
+
{ $assoc.on=$cond.cond; }
|
|
1916
|
+
)
|
|
1917
|
+
)?
|
|
1911
1918
|
;
|
|
1912
1919
|
|
|
1913
|
-
|
|
1914
|
-
@after { this.attachLocation( $wfe ); }
|
|
1920
|
+
bracedSelectItemListDef[ query ]
|
|
1915
1921
|
:
|
|
1916
|
-
{ $
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1922
|
+
'{' { $query.columns = this.createArray(); }
|
|
1923
|
+
(
|
|
1924
|
+
selectItemDef[ $query.columns ]
|
|
1925
|
+
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1926
|
+
selectItemDef[ $query.columns ]
|
|
1927
|
+
)*
|
|
1928
|
+
)?
|
|
1929
|
+
'}' { this.finalizeDictOrArray( $query.columns ); }
|
|
1924
1930
|
;
|
|
1925
1931
|
|
|
1926
|
-
|
|
1927
|
-
@after{ /* #ATN 1 */ }
|
|
1932
|
+
selectItemInlineList[ art, clause ]
|
|
1928
1933
|
:
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
{ $wfb = { op: this.valueWithTokenLocation( 'following', $n, $f ), args: [ this.numberLiteral( $n ) ]} }
|
|
1938
|
-
|
|
|
1939
|
-
{ $wfb = {} }
|
|
1940
|
-
windowFrameStartSpec [ $wfb ]
|
|
1934
|
+
'{' { $art[$clause] = this.createArray(); }
|
|
1935
|
+
(
|
|
1936
|
+
selectItemInlineDef[ $art[$clause] ]
|
|
1937
|
+
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1938
|
+
selectItemInlineDef[ $art[$clause] ]
|
|
1939
|
+
)*
|
|
1940
|
+
)?
|
|
1941
|
+
'}' { this.finalizeDictOrArray( $art[$clause] ); }
|
|
1941
1942
|
;
|
|
1942
1943
|
|
|
1943
|
-
|
|
1944
|
-
@after
|
|
1944
|
+
selectItemInlineDef[ outer ] locals[ art ]
|
|
1945
|
+
@after{ if ($art) this.attachLocation( $art ); }
|
|
1945
1946
|
:
|
|
1946
|
-
|
|
1947
|
-
{
|
|
1948
|
-
$wf.op = this.valueWithTokenLocation( 'unboundedPreceding', $u, $p );
|
|
1949
|
-
$wf.args = [];
|
|
1950
|
-
}
|
|
1951
|
-
|
|
|
1952
|
-
n=Number p=PRECEDING
|
|
1953
|
-
{
|
|
1954
|
-
$wf.op = this.valueWithTokenLocation( 'preceding', $p );
|
|
1955
|
-
$wf.args = [ this.numberLiteral( $n ) ];
|
|
1956
|
-
}
|
|
1947
|
+
star='*'
|
|
1948
|
+
{ $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
|
|
1957
1949
|
|
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
$wf.args = [];
|
|
1962
|
-
}
|
|
1950
|
+
{ $art = {};; this.docComment( $art ); }
|
|
1951
|
+
annotationAssignment_atn[ $art ]*
|
|
1952
|
+
selectItemDefBody[ $art, $outer ]
|
|
1963
1953
|
;
|
|
1964
1954
|
|
|
1965
|
-
|
|
1966
|
-
@after { this.attachLocation( $over ); }
|
|
1955
|
+
orderByClause[ inQuery ] returns [ query ]
|
|
1967
1956
|
:
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
|
|
1972
|
-
( wf=windowFrameClause { $over.args.push( $wf.wf ); } )?
|
|
1973
|
-
')'
|
|
1957
|
+
ORDER BY { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
|
|
1958
|
+
ob1=orderBySpec { $query.orderBy = [ $ob1.ob ]; }
|
|
1959
|
+
( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
|
|
1974
1960
|
;
|
|
1975
1961
|
|
|
1976
1962
|
limitClause[ inQuery ] returns [ query ]
|
|
@@ -1994,87 +1980,6 @@ orderBySpec returns[ ob ]
|
|
|
1994
1980
|
)?
|
|
1995
1981
|
;
|
|
1996
1982
|
|
|
1997
|
-
queryTerm returns[ query ]
|
|
1998
|
-
@after{ this.attachLocation($query); }
|
|
1999
|
-
:
|
|
2000
|
-
qt1=queryPrimary { $query = $qt1.query; }
|
|
2001
|
-
(
|
|
2002
|
-
intersect=INTERSECT quantifier=DISTINCT?
|
|
2003
|
-
qt=queryPrimary
|
|
2004
|
-
{ $query = this.leftAssocBinaryOp( $query, $intersect, $quantifier, $qt.query );
|
|
2005
|
-
$ctx.quantifier = null; } // reset for loop
|
|
2006
|
-
)*
|
|
2007
|
-
;
|
|
2008
|
-
|
|
2009
|
-
queryPrimary returns[ query = {} ]
|
|
2010
|
-
@after { this.attachLocation($query); }
|
|
2011
|
-
:
|
|
2012
|
-
open='(' qe=queryExpression close=')'
|
|
2013
|
-
{ $query = this.surroundByParens( $qe.query, $open, $close ); }
|
|
2014
|
-
|
|
|
2015
|
-
select=SELECT
|
|
2016
|
-
{ $query = { op: this.valueWithTokenLocation( 'SELECT', $select ), location: this.startLocation() }; }
|
|
2017
|
-
(
|
|
2018
|
-
FROM querySource[ $query ]
|
|
2019
|
-
(
|
|
2020
|
-
mixin=MIXIN '{' { $query.mixin = this.createDict(); }
|
|
2021
|
-
mixinElementDef[ $query ]*
|
|
2022
|
-
'}' { this.finalizeDictOrArray( $query.mixin ); }
|
|
2023
|
-
INTO
|
|
2024
|
-
)?
|
|
2025
|
-
( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
|
|
2026
|
-
{ $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
|
|
2027
|
-
)?
|
|
2028
|
-
bracedSelectItemListDef[ $query, 'columns' ]?
|
|
2029
|
-
excludingClause[ $query ]?
|
|
2030
|
-
|
|
|
2031
|
-
( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
|
|
2032
|
-
{ $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
|
|
2033
|
-
)?
|
|
2034
|
-
{ $query.columns = []; } // set it early to avoid "wildcard" errors
|
|
2035
|
-
selectItemDef[ $query.columns ]
|
|
2036
|
-
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
2037
|
-
selectItemDef[ $query.columns ]
|
|
2038
|
-
)*
|
|
2039
|
-
FROM querySource[ $query ]
|
|
2040
|
-
)
|
|
2041
|
-
( WHERE cond=condition { $query.where = $cond.cond; } )?
|
|
2042
|
-
(
|
|
2043
|
-
GROUP BY
|
|
2044
|
-
e1=expression { $query.groupBy = [ $e1.expr ]; }
|
|
2045
|
-
( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
|
|
2046
|
-
)?
|
|
2047
|
-
( HAVING having=condition { $query.having = $having.cond; } )?
|
|
2048
|
-
;
|
|
2049
|
-
|
|
2050
|
-
querySource[ query ]
|
|
2051
|
-
@after { this.attachLocation($query.from); }
|
|
2052
|
-
:
|
|
2053
|
-
t1=tableExpression { $query.from = $t1.table; }
|
|
2054
|
-
(
|
|
2055
|
-
{ const location = this.tokenLocation( this.getCurrentToken() );
|
|
2056
|
-
$query.from = { op: { val: 'join', location },
|
|
2057
|
-
join: { val: 'cross', location },
|
|
2058
|
-
args: [$t1.table] }; }
|
|
2059
|
-
( ',' tn=tableExpression { if ($tn.table) $query.from.args.push( $tn.table ); } )+
|
|
2060
|
-
)?
|
|
2061
|
-
;
|
|
2062
|
-
|
|
2063
|
-
tableExpression returns[ table ] // TableOrJoin
|
|
2064
|
-
@after { this.attachLocation($table); }
|
|
2065
|
-
:
|
|
2066
|
-
qt=tableTerm { $table = $qt.table; }
|
|
2067
|
-
(
|
|
2068
|
-
join=joinOp[ $table ] { $table = $join.table; }
|
|
2069
|
-
te=tableExpression
|
|
2070
|
-
{ if (!$table) { $table = {}; } else if ($te.table) $table.args.push( $te.table ); }
|
|
2071
|
-
ON cond=condition { $table.on = $cond.cond; }
|
|
2072
|
-
|
|
|
2073
|
-
crj=CROSS jn=JOIN tt=tableTerm
|
|
2074
|
-
{ if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
|
|
2075
|
-
)*
|
|
2076
|
-
;
|
|
2077
|
-
|
|
2078
1983
|
joinOp[ left ] returns[ table ] locals [ join ]
|
|
2079
1984
|
:
|
|
2080
1985
|
( op=JOIN { $join = 'inner'; }
|
|
@@ -2122,55 +2027,7 @@ joinCardinality returns [ joinCard ]
|
|
|
2122
2027
|
)
|
|
2123
2028
|
;
|
|
2124
2029
|
|
|
2125
|
-
|
|
2126
|
-
@after{ /* #ATN 1 */ this.attachLocation($table); }
|
|
2127
|
-
:
|
|
2128
|
-
{ $table = { path: [], scope: 0 }; }
|
|
2129
|
-
fromPath[ $table, 'artref']
|
|
2130
|
-
( ':'
|
|
2131
|
-
{ $table.scope = $table.path.length; }
|
|
2132
|
-
fromPath[ $table, 'ref']
|
|
2133
|
-
)?
|
|
2134
|
-
( AS n1=ident['FromAlias'] { $table.name = $n1.id }
|
|
2135
|
-
| n2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $n2.id ); }
|
|
2136
|
-
// if we would use rule `ident`, we would either had to make all JOIN
|
|
2137
|
-
// kinds reserved or introduce ATN
|
|
2138
|
-
)?
|
|
2139
|
-
// ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
|
|
2140
|
-
{ if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
|
|
2141
|
-
|
|
|
2142
|
-
open='('
|
|
2143
|
-
// #ATN: The following alternative is not LL1, because both can start with
|
|
2144
|
-
// left-paren, but queryExpression has SELECT after initial left-parens
|
|
2145
|
-
(
|
|
2146
|
-
qe=queryExpression close=')'
|
|
2147
|
-
{ $table = this.surroundByParens( $qe.query, $open, $close, true ); }
|
|
2148
|
-
( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table aliass
|
|
2149
|
-
| a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
|
|
2150
|
-
// not using ident` to have a similar behavior to above
|
|
2151
|
-
)
|
|
2152
|
-
|
|
|
2153
|
-
te=tableExpression close=')'
|
|
2154
|
-
{ $table = this.surroundByParens( $te.table, $open, $close ); }
|
|
2155
|
-
)
|
|
2156
|
-
;
|
|
2157
|
-
|
|
2158
|
-
fromPath[ qp, idkind ]
|
|
2159
|
-
@after{ this.attachLocation($qp); }
|
|
2160
|
-
:
|
|
2161
|
-
id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
2162
|
-
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
2163
|
-
| cardinalityAndFilter[ $id.id ]
|
|
2164
|
-
)?
|
|
2165
|
-
(
|
|
2166
|
-
'.' id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
2167
|
-
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
2168
|
-
| cardinalityAndFilter[ $id.id ]
|
|
2169
|
-
)?
|
|
2170
|
-
)*
|
|
2171
|
-
;
|
|
2172
|
-
|
|
2173
|
-
// Conditions and expressions ------------------------------------------------
|
|
2030
|
+
// Conditions and expressions ---------------------------------------------------
|
|
2174
2031
|
|
|
2175
2032
|
// With "separate" `condition` and `expression` rules, we have long LL
|
|
2176
2033
|
// ambiguities (not so with LALR used in Bison) with initial left parentheses:
|
|
@@ -2186,221 +2043,214 @@ fromPath[ qp, idkind ]
|
|
|
2186
2043
|
// be then ensured by code (either in actions of the grammar or in a check
|
|
2187
2044
|
// phase - to be discussed).
|
|
2188
2045
|
//
|
|
2046
|
+
// We cannot generally allow `condition` where `expression` is written:
|
|
2047
|
+
// - IN is also used in non-standard function `args` and as predicate:
|
|
2048
|
+
// - AND is boolean operator and also used for BETWEEN…AND
|
|
2049
|
+
//
|
|
2189
2050
|
// ANTLR4s left-recursion feature cannot be used as we will have rule
|
|
2190
2051
|
// arguments.
|
|
2191
2052
|
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2053
|
+
conditionEOF returns [ cond ]
|
|
2054
|
+
:
|
|
2055
|
+
c=condition { $cond = $c.cond; } EOF
|
|
2056
|
+
;
|
|
2057
|
+
|
|
2058
|
+
condition returns [ cond ] locals[ args = [] ]
|
|
2059
|
+
@after{ $cond = this.argsExpression( $args ); }
|
|
2198
2060
|
:
|
|
2199
2061
|
c1=conditionAnd { $args.push($c1.cond); }
|
|
2200
|
-
(
|
|
2062
|
+
(
|
|
2063
|
+
or=OR { this.pushXprToken( $args ); }
|
|
2064
|
+
c2=conditionAnd { $args.push($c2.cond); }
|
|
2065
|
+
)*
|
|
2201
2066
|
;
|
|
2202
2067
|
|
|
2203
|
-
conditionAnd returns [ cond ] locals
|
|
2204
|
-
@after{
|
|
2205
|
-
$cond = ($args.length === 1)
|
|
2206
|
-
? $args[0]
|
|
2207
|
-
: this.attachLocation({ op: $andl[0], args: $args });
|
|
2208
|
-
}
|
|
2068
|
+
conditionAnd returns [ cond ] locals[ args = [] ]
|
|
2069
|
+
@after{ $cond = this.argsExpression( $args, null, 'and' ); } // 'and' used by A2J and checks
|
|
2209
2070
|
:
|
|
2210
2071
|
c1=conditionTerm { $args.push($c1.cond); }
|
|
2211
|
-
(
|
|
2072
|
+
(
|
|
2073
|
+
and=AND { this.pushXprToken( $args ); }
|
|
2074
|
+
c2=conditionTerm { $args.push($c2.cond); }
|
|
2075
|
+
)*
|
|
2212
2076
|
;
|
|
2213
2077
|
|
|
2214
2078
|
// Note: New operators need to be added to functionExpressionOperatorsRequireParentheses[] in toCdl.js.
|
|
2215
|
-
conditionTerm returns [ cond ]
|
|
2216
|
-
@after{
|
|
2217
|
-
if ($cond) { this.attachLocation($cond); } else { $cond = $expr.expr; }
|
|
2218
|
-
}
|
|
2079
|
+
conditionTerm returns [ cond ] locals[ args = [] ]
|
|
2080
|
+
@after{ $cond = this.argsExpression( $args, null, '=' ); }// op: '=' used by A2J and checks
|
|
2219
2081
|
:
|
|
2220
|
-
nt=NOT
|
|
2221
|
-
|
|
2082
|
+
nt=NOT { this.pushXprToken( $args ); }
|
|
2083
|
+
ct=conditionTerm { $args.push( $ct.cond ); }
|
|
2222
2084
|
|
|
|
2223
|
-
|
|
2085
|
+
EXISTS { this.pushXprToken( $args ); }
|
|
2224
2086
|
(
|
|
2225
2087
|
open='(' qe=queryExpression close=')'
|
|
2226
|
-
{ $
|
|
2227
|
-
args: [ this.surroundByParens( $qe.query, $open, $close, true ) ] }; }
|
|
2088
|
+
{ $args.push( this.surroundByParens( $qe.query, $open, $close, true ) ); }
|
|
2228
2089
|
|
|
|
2229
2090
|
qm=( HideAlternatives | '?' )
|
|
2230
|
-
{ $
|
|
2231
|
-
|
|
2232
|
-
] };
|
|
2233
|
-
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', name: '?' } );
|
|
2091
|
+
{ $args.push( { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' } );
|
|
2092
|
+
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
|
|
2234
2093
|
}
|
|
2235
2094
|
|
|
|
2236
|
-
ep=valuePath[ 'ref' ]
|
|
2237
|
-
{ $ep.qp['$'+'expected'] = 'exists';
|
|
2238
|
-
$cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [ $ep.qp ] };
|
|
2239
|
-
}
|
|
2095
|
+
ep=valuePath[ 'ref' ] { $args.push( $ep.qp ); }
|
|
2096
|
+
{ $ep.qp['$'+'expected'] = 'exists'; } // hm, really in parser? what about CSN input?
|
|
2240
2097
|
)
|
|
2241
2098
|
|
|
|
2242
|
-
expr=expression
|
|
2099
|
+
expr=expression { $args.push( $expr.expr ); }
|
|
2243
2100
|
(
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
{ $cond.quantifier = this.valueWithTokenLocation( $asa.text.toLowerCase(), $asa ); }
|
|
2248
|
-
)?
|
|
2249
|
-
e2=expression { $cond.args.push($e2.expr); }
|
|
2101
|
+
( '=' | '<>' | '>' | '>=' | '<' | '<=' | '!=' ) { this.pushXprToken( $args ); }
|
|
2102
|
+
( ( ANY | SOME | ALL ) { this.pushXprToken( $args ); } )?
|
|
2103
|
+
e2=expression { $args.push( $e2.expr ); }
|
|
2250
2104
|
|
|
|
2251
|
-
IS (
|
|
2252
|
-
|
|
2105
|
+
IS { this.pushXprToken( $args ); }
|
|
2106
|
+
( NOT { this.pushXprToken( $args ); } )?
|
|
2107
|
+
NULL { this.pushXprToken( $args ); }
|
|
2253
2108
|
|
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
{ if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
|
|
2109
|
+
NOT { this.pushXprToken( $args ); }
|
|
2110
|
+
predicate[ $args ]
|
|
2257
2111
|
|
|
|
2258
|
-
|
|
2259
|
-
predicate[ $cond, false ]
|
|
2260
|
-
{ if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
|
|
2112
|
+
predicate[ $args ]
|
|
2261
2113
|
)? // optional: for conditions in parentheses
|
|
2262
2114
|
;
|
|
2263
2115
|
|
|
2264
2116
|
// Note: New operators need to be added to functionExpressionOperatorsRequireParentheses[] in toCdl.js.
|
|
2265
|
-
predicate[
|
|
2266
|
-
// As an alternative, we could have a `negated` properties for the operations
|
|
2267
|
-
// `isNull`(!), `in`, `between` and `like` (or produce the same AST as for
|
|
2268
|
-
// NOT (a BETWEEN b AND c)
|
|
2117
|
+
predicate[ args ]
|
|
2269
2118
|
:
|
|
2270
|
-
|
|
2271
|
-
|
|
2119
|
+
IN { this.pushXprToken( $args ); }
|
|
2120
|
+
// TODO: do we really allow it not to start with `(`? - TODO: try with warning
|
|
2121
|
+
e1=expression { $args.push( this.secureParens( $e1.expr ) ); } // including ExpressionList
|
|
2272
2122
|
|
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
AND
|
|
2123
|
+
BETWEEN { this.pushXprToken( $args ); }
|
|
2124
|
+
e2=expression { $args.push( $e2.expr ); }
|
|
2125
|
+
AND { this.pushXprToken( $args ); }
|
|
2126
|
+
e3=expression { $args.push( $e3.expr ); }
|
|
2276
2127
|
|
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
( ESCAPE
|
|
2128
|
+
LIKE { this.pushXprToken( $args ); }
|
|
2129
|
+
e4=expression { $args.push( $e4.expr ); }
|
|
2130
|
+
( ESCAPE { this.pushXprToken( $args ); }
|
|
2131
|
+
e5=expression { $args.push( $e5.expr ); }
|
|
2132
|
+
)?
|
|
2280
2133
|
;
|
|
2281
2134
|
|
|
2282
|
-
expression returns [ expr ]
|
|
2283
|
-
@after{
|
|
2135
|
+
expression returns [ expr ] locals [ args = [] ]
|
|
2136
|
+
@after{ $expr = this.argsExpression( $args ); }
|
|
2284
2137
|
:
|
|
2285
|
-
e1=expressionSum { $
|
|
2138
|
+
e1=expressionSum { $args.push( $e1.expr ); }
|
|
2286
2139
|
(
|
|
2287
|
-
|
|
2288
|
-
{
|
|
2289
|
-
$expr = {
|
|
2290
|
-
op: this.valueWithTokenLocation( '||', $or ), args: [$expr, $e2.expr],
|
|
2291
|
-
location: this.combinedLocation( $expr, $e2.expr ) };
|
|
2292
|
-
}
|
|
2140
|
+
'||' { this.pushXprToken( $args ); }
|
|
2141
|
+
e2=expressionSum { $args.push( $e2.expr ); }
|
|
2293
2142
|
)*
|
|
2294
2143
|
;
|
|
2295
2144
|
|
|
2296
|
-
expressionSum returns [ expr ]
|
|
2297
|
-
@after{
|
|
2145
|
+
expressionSum returns [ expr ] locals [ args = [] ]
|
|
2146
|
+
@after{ $expr = this.argsExpression( $args ); }
|
|
2298
2147
|
:
|
|
2299
|
-
e1=expressionFactor { $
|
|
2148
|
+
e1=expressionFactor { $args.push( $e1.expr ); }
|
|
2300
2149
|
(
|
|
2301
|
-
|
|
2302
|
-
{
|
|
2303
|
-
$expr = {
|
|
2304
|
-
op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
|
|
2305
|
-
location: this.combinedLocation( $expr, $e2.expr ) };
|
|
2306
|
-
}
|
|
2150
|
+
( '+' | '-' ) { this.pushXprToken( $args ); }
|
|
2151
|
+
e2=expressionFactor { $args.push( $e2.expr ); }
|
|
2307
2152
|
)*
|
|
2308
2153
|
;
|
|
2309
2154
|
|
|
2310
|
-
expressionFactor returns [ expr ]
|
|
2311
|
-
@after{
|
|
2155
|
+
expressionFactor returns [ expr ] locals [ args = [] ]
|
|
2156
|
+
@after{ $expr = this.argsExpression( $args ); }
|
|
2312
2157
|
:
|
|
2313
|
-
e1=expressionTerm { $
|
|
2158
|
+
e1=expressionTerm { $args.push( $e1.expr ); }
|
|
2314
2159
|
(
|
|
2315
|
-
|
|
2316
|
-
{
|
|
2317
|
-
$expr = {
|
|
2318
|
-
op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
|
|
2319
|
-
location: this.combinedLocation( $expr, $e2.expr ) };
|
|
2320
|
-
}
|
|
2160
|
+
( '*' | '/' ) { this.pushXprToken( $args ); }
|
|
2161
|
+
e2=expressionTerm { $args.push( $e2.expr ); }
|
|
2321
2162
|
)*
|
|
2322
2163
|
;
|
|
2323
2164
|
|
|
2324
|
-
expressionTerm returns [ expr ] locals [
|
|
2325
|
-
@after{ /* #ATN 1 */ this.
|
|
2165
|
+
expressionTerm returns [ expr ] locals [ args = [] ]
|
|
2166
|
+
@after{ /* #ATN 1 */ $expr = this.argsExpression( $args ); }
|
|
2326
2167
|
:
|
|
2327
|
-
|
|
2328
|
-
|
|
2168
|
+
( '+' | '-' ) { this.pushXprToken( $args ); }
|
|
2169
|
+
e1=expressionTerm // prefix op or part of the number
|
|
2170
|
+
{ this.signedExpression( $args, $e1.expr ); }
|
|
2329
2171
|
|
|
|
2172
|
+
val=literalValue { $args.push( $val.val ); }
|
|
2173
|
+
|
|
|
2174
|
+
sf=specialFunction { $args.push( $sf.ret ); }
|
|
2175
|
+
|
|
|
2176
|
+
CASE { this.pushXprToken( $args ); }
|
|
2330
2177
|
(
|
|
2331
|
-
|
|
2332
|
-
{ $
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
ca=CASE
|
|
2338
|
-
{ $expr = { op : this.valueWithTokenLocation( 'case', $ca ), args: [] }; }
|
|
2339
|
-
(
|
|
2340
|
-
e2=expression { $expr.args.push($e2.expr); }
|
|
2341
|
-
( ow=WHEN ew=expression THEN e3=expression
|
|
2342
|
-
{ $expr.args.push( this.createPrefixOp( $ow, [ $ew.expr, $e3.expr ] ) ); }
|
|
2343
|
-
)+
|
|
2344
|
-
|
|
|
2345
|
-
( ow=WHEN c=condition THEN e3=expression
|
|
2346
|
-
{ $expr.args.push( this.createPrefixOp( $ow, [ $c.cond, $e3.expr ] ) ); }
|
|
2347
|
-
)+
|
|
2348
|
-
)
|
|
2349
|
-
( el=ELSE e4=expression
|
|
2350
|
-
{ $expr.args.push( this.createPrefixOp( $el, [ $e4.expr ] ) ); }
|
|
2351
|
-
)?
|
|
2352
|
-
END
|
|
2353
|
-
|
|
|
2354
|
-
ne=NEW nqp=valuePath[ 'ref', null] // token rewrite for NEW
|
|
2355
|
-
// please note: there will be no compiler-supported code completion after NEW
|
|
2356
|
-
{ $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
|
|
2357
|
-
this.error( 'syntax-unsupported-new', $ne, { keyword: $ne.text }, '$(KEYWORD) is not supported' ); }
|
|
2178
|
+
e2=expression { $args.push( $e2.expr ); }
|
|
2179
|
+
( WHEN { this.pushXprToken( $args ); }
|
|
2180
|
+
ew=expression { $args.push( $ew.expr ); }
|
|
2181
|
+
THEN { this.pushXprToken( $args ); }
|
|
2182
|
+
e3=expression { $args.push( $e3.expr ); }
|
|
2183
|
+
)+
|
|
2358
2184
|
|
|
|
2359
|
-
vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
|
|
2360
|
-
{ this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
|
|
2361
2185
|
(
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2186
|
+
WHEN { this.pushXprToken( $args ); }
|
|
2187
|
+
c=condition { $args.push( $c.cond ); }
|
|
2188
|
+
THEN { this.pushXprToken( $args ); }
|
|
2189
|
+
e3=expression { $args.push( $e3.expr ); }
|
|
2190
|
+
)+
|
|
2191
|
+
)
|
|
2192
|
+
(
|
|
2193
|
+
ELSE { this.pushXprToken( $args ); }
|
|
2194
|
+
e4=expression { $args.push( $e4.expr ); }
|
|
2195
|
+
)?
|
|
2196
|
+
END { this.pushXprToken( $args ); }
|
|
2197
|
+
|
|
|
2198
|
+
ne=NEW { this.pushXprToken( $args ); } // token rewrite for NEW
|
|
2199
|
+
// please note: there will be no compiler-supported code completion after NEW
|
|
2200
|
+
{ $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
|
|
2201
|
+
this.error( 'syntax-unsupported-new', $ne, { keyword: $ne.text }, '$(KEYWORD) is not supported' ); }
|
|
2202
|
+
nqp=valuePath[ 'ref', null ]
|
|
2203
|
+
|
|
|
2204
|
+
vp=valuePath[ 'ref', null ] { $args.push( this.valuePathAst( $vp.qp ) ); }
|
|
2205
|
+
{ this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
|
|
2206
|
+
(
|
|
2207
|
+
{ $args[0].suffix = []; }
|
|
2208
|
+
OVER { this.pushXprToken( $args[0].suffix ); }
|
|
2209
|
+
open='(' over=overClause close=')'
|
|
2210
|
+
{ $args[0].suffix.push( this.surroundByParens( $over.over, $open, $close ) ); }
|
|
2211
|
+
)?
|
|
2212
|
+
|
|
|
2213
|
+
':'
|
|
2214
|
+
(
|
|
2215
|
+
vp=valuePath[ 'paramref', this.startLocation() ]
|
|
2216
|
+
{{ const par = $vp.qp;; par.scope = 'param';; $args.push( par ); }}
|
|
2373
2217
|
|
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
{ $expr = { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' };
|
|
2378
|
-
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', name: '?' } );
|
|
2218
|
+
pp=Number
|
|
2219
|
+
{ $args.push( { param: this.numberLiteral( $pp ), scope: 'param' } );
|
|
2220
|
+
this.csnParseOnly( 'syntax-unsupported-param', [ $pp ], { '#': 'positional', code: ':' + $pp.text } );
|
|
2379
2221
|
}
|
|
2222
|
+
)
|
|
2223
|
+
|
|
|
2224
|
+
qm= '?' // is automatically not mentioned as CC candidate
|
|
2225
|
+
// if we have an HideAlternatives here, we would block it to use it in
|
|
2226
|
+
// parallel to an expression (would produce adaptivePredict() otherwise)
|
|
2227
|
+
{ $args.push( { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' } );
|
|
2228
|
+
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
|
|
2229
|
+
}
|
|
2230
|
+
|
|
|
2231
|
+
open='('
|
|
2232
|
+
// #ATN: The following alternative is not LL1, because both can start with
|
|
2233
|
+
// left-paren, but queryExpression has SELECT after initial left-parens
|
|
2234
|
+
(
|
|
2235
|
+
qe=queryExpression close=')'
|
|
2236
|
+
{ $args.push( this.surroundByParens( $qe.query, $open, $close, true ) ); }
|
|
2380
2237
|
|
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
{
|
|
2394
|
-
if ($expr.length > 1)
|
|
2395
|
-
$expr = { op: this.valueWithTokenLocation( ',', $open ), args: $expr };
|
|
2396
|
-
else if ($expr[0]) // can be `null` if condition failed to parse
|
|
2397
|
-
$expr = this.surroundByParens( $expr[0], $open, $close );
|
|
2398
|
-
}
|
|
2399
|
-
)
|
|
2238
|
+
c1=condition { $args = [ $c1.cond ]; }
|
|
2239
|
+
( ',' { if ($args.length > 1 && this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
2240
|
+
cn=expression { if ($cn.expr) $args.push($cn.expr); }
|
|
2241
|
+
)*
|
|
2242
|
+
close=')'
|
|
2243
|
+
{
|
|
2244
|
+
if ($args.length > 1)
|
|
2245
|
+
$args = [ { op: this.valueWithTokenLocation( 'list', $open ), args: $args,
|
|
2246
|
+
location: this.tokenLocation( $open, $close ) } ];
|
|
2247
|
+
else if ($args[0]) // can be `null` if condition failed to parse
|
|
2248
|
+
$args = [ this.surroundByParens( $args[0], $open, $close ) ];
|
|
2249
|
+
}
|
|
2400
2250
|
)
|
|
2401
2251
|
;
|
|
2402
2252
|
|
|
2403
|
-
specialFunction returns [ ret = {
|
|
2253
|
+
specialFunction returns [ ret = {} ] locals[ art = {} ]
|
|
2404
2254
|
:
|
|
2405
2255
|
ca=CAST '(' // see createArray() in action
|
|
2406
2256
|
{
|
|
@@ -2417,9 +2267,21 @@ specialFunction returns [ ret = { } ] locals[ art = {} ]
|
|
|
2417
2267
|
')' { this.finalizeDictOrArray( $ret.args ); }
|
|
2418
2268
|
;
|
|
2419
2269
|
|
|
2420
|
-
//
|
|
2421
|
-
|
|
2422
|
-
|
|
2270
|
+
// Paths and functions: ---------------------------------------------------------
|
|
2271
|
+
|
|
2272
|
+
simplePath[ art, category ] locals[ _sync = 'nop' ]
|
|
2273
|
+
@after { this.attachLocation($art); }
|
|
2274
|
+
// Due to error recovery, rule `ident` can return with value `null`. Set the
|
|
2275
|
+
// path as broken in this case.
|
|
2276
|
+
:
|
|
2277
|
+
head=ident[ $category ]
|
|
2278
|
+
{ if (!$art.path) $art.path = []; this.pushIdent( $art.path, $head.id );
|
|
2279
|
+
if ($category === 'artref') $art.scope = 0;
|
|
2280
|
+
}
|
|
2281
|
+
(
|
|
2282
|
+
'.' tail=ident[ $category ] { this.pushIdent( $art.path, $tail.id ); }
|
|
2283
|
+
)*
|
|
2284
|
+
;
|
|
2423
2285
|
|
|
2424
2286
|
valuePath[ category, location = null ] returns[ qp = { path: [] } ] locals[ _sync = 'nop' ]
|
|
2425
2287
|
@init { $qp.location = location || this.startLocation(); }
|
|
@@ -2439,19 +2301,6 @@ valuePath[ category, location = null ] returns[ qp = { path: [] } ] locals[ _syn
|
|
|
2439
2301
|
)*
|
|
2440
2302
|
;
|
|
2441
2303
|
|
|
2442
|
-
fromArguments[ pathStep ]
|
|
2443
|
-
@init{ if (!$pathStep) $pathStep = {}; } // grammar robustness, see test/negative/parser/NamedExpression.cds
|
|
2444
|
-
:
|
|
2445
|
-
'(' { $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; } // necessary?
|
|
2446
|
-
name=ident['paramname'] ':'
|
|
2447
|
-
namedExpression[ $pathStep, $name.id ]
|
|
2448
|
-
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
2449
|
-
name=ident['paramname'] ':'
|
|
2450
|
-
namedExpression[ $pathStep, $name.id ]
|
|
2451
|
-
)*
|
|
2452
|
-
')' { this.finalizeDictOrArray( $pathStep.args ); }
|
|
2453
|
-
;
|
|
2454
|
-
|
|
2455
2304
|
pathArguments[ pathStep, considerSpecial ]
|
|
2456
2305
|
@init{
|
|
2457
2306
|
if (!$pathStep) $pathStep = {}; // grammar robustness, see test/negative/parser/NamedExpression.cds
|
|
@@ -2491,11 +2340,9 @@ pathArguments[ pathStep, considerSpecial ]
|
|
|
2491
2340
|
funcExpression[ $pathStep, $considerSpecial ]
|
|
2492
2341
|
)*
|
|
2493
2342
|
// Note: We can't move this into funcExpression, or we would increase the ATN count because of `,` amiguity.
|
|
2494
|
-
( ob=
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
$pathStep.args[$pathStep.args.length - 1] = $ob.expr;
|
|
2498
|
-
}
|
|
2343
|
+
( ob=funcOrderByClause[ [ $pathStep.args[$pathStep.args.length - 1] ] ]
|
|
2344
|
+
// Remove the last entry which was copied to $ob.expr and push $ob.expr:
|
|
2345
|
+
{ $pathStep.args[$pathStep.args.length - 1] = $ob.expr; }
|
|
2499
2346
|
)?
|
|
2500
2347
|
|
|
|
2501
2348
|
{ $pathStep.args = this.createArray(); }
|
|
@@ -2517,32 +2364,29 @@ namedExpression[ pathStep, id ]
|
|
|
2517
2364
|
}
|
|
2518
2365
|
;
|
|
2519
2366
|
|
|
2520
|
-
funcExpression[ pathStep, considerSpecial ] locals[ args ]
|
|
2367
|
+
funcExpression[ pathStep, considerSpecial ] locals[ args = [] ]
|
|
2521
2368
|
@init { this.prepareGenericKeywords( $considerSpecial ); }
|
|
2369
|
+
@after{ $pathStep.args.push( this.argsExpression( $args, true ) ); }
|
|
2522
2370
|
:
|
|
2523
2371
|
(
|
|
2524
2372
|
expr=expression
|
|
2525
|
-
{ $
|
|
2373
|
+
{ $args.push( $expr.expr ); }
|
|
2526
2374
|
|
|
|
2527
2375
|
GenericExpr // keyword as replacement for expression, like '*'
|
|
2528
|
-
{
|
|
2376
|
+
{ this.pushXprToken( $args ); }
|
|
2529
2377
|
|
|
|
2530
2378
|
GenericIntro // keyword as introduction of expression, like DISTINCT
|
|
2531
|
-
{
|
|
2532
|
-
expr=expression
|
|
2533
|
-
{ $args = this.setLastAsXpr( $pathStep.args );
|
|
2534
|
-
$args.push( $expr.expr ); }
|
|
2379
|
+
{ this.pushXprToken( $args ); }
|
|
2380
|
+
expr=expression { $args.push( $expr.expr ); }
|
|
2535
2381
|
|
|
|
2536
2382
|
// Rule 'pathArguments' makes a decision based on the first two lookahead
|
|
2537
2383
|
// tokens of this rule → we need to list tokens which would be changed to
|
|
2538
2384
|
// GenericExpr or GenericIntro, and are not already covered by 'expression'
|
|
2539
2385
|
{ this.reportErrorForGenericKeyword(); }
|
|
2540
|
-
( HideAlternatives | '*' | ALL | DISTINCT )
|
|
2386
|
+
( HideAlternatives | '*' | ALL | DISTINCT ) { this.pushXprToken( $args ); }
|
|
2541
2387
|
// now continue parsing like GenericExpr:
|
|
2542
|
-
{ $pathStep.args.push( this.xprToken() ); }
|
|
2543
2388
|
)
|
|
2544
2389
|
(
|
|
2545
|
-
{ if (!$args) $args = this.setLastAsXpr( $pathStep.args ); }
|
|
2546
2390
|
(
|
|
2547
2391
|
{ this.prepareGenericKeywords( $considerSpecial, 'separator' ); }
|
|
2548
2392
|
(
|
|
@@ -2556,26 +2400,108 @@ funcExpression[ pathStep, considerSpecial ] locals[ args ]
|
|
|
2556
2400
|
{ this.reportErrorForGenericKeyword(); }
|
|
2557
2401
|
( HideAlternatives | Identifier | FROM | IN | WITH | GROUP )
|
|
2558
2402
|
)
|
|
2559
|
-
{
|
|
2560
|
-
this.prepareGenericKeywords( $considerSpecial, 'expr' );
|
|
2561
|
-
}
|
|
2403
|
+
{ this.pushXprToken( $args );
|
|
2404
|
+
this.prepareGenericKeywords( $considerSpecial, 'expr' ); }
|
|
2562
2405
|
(
|
|
2563
|
-
expr=expression
|
|
2564
|
-
{ $args.push( $expr.expr ); }
|
|
2406
|
+
expr=expression { $args.push( $expr.expr ); }
|
|
2565
2407
|
|
|
|
2566
|
-
GenericExpr
|
|
2567
|
-
{ $args.push( this.xprToken() ); }
|
|
2408
|
+
GenericExpr { this.pushXprToken( $args ); }
|
|
2568
2409
|
|
|
|
2569
2410
|
{ this.reportErrorForGenericKeyword(); }
|
|
2570
2411
|
// Again, we need to list tokens which could make it to GenericExpr
|
|
2571
2412
|
// and which do not start an expression
|
|
2572
|
-
( HideAlternatives | ALL )
|
|
2573
|
-
{ $args.push( this.xprToken() ); }
|
|
2413
|
+
( HideAlternatives | ALL ) { this.pushXprToken( $args ); }
|
|
2574
2414
|
)
|
|
2575
2415
|
)+
|
|
2576
2416
|
)?
|
|
2577
2417
|
;
|
|
2578
2418
|
|
|
2419
|
+
overClause returns[ over ] locals[ args = [] ]
|
|
2420
|
+
@after{ $over = this.argsExpression( $args ); }
|
|
2421
|
+
:
|
|
2422
|
+
// TODO: empty really allowed?
|
|
2423
|
+
( PARTITION { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
|
|
2424
|
+
pb=partitionByClause { $args.push( $pb.expr ); }
|
|
2425
|
+
)?
|
|
2426
|
+
( ORDER { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
|
|
2427
|
+
ob=exprOrderByClause { $args.push( $ob.expr ); }
|
|
2428
|
+
)?
|
|
2429
|
+
( ROWS { this.pushXprToken( $args ); }
|
|
2430
|
+
wf=windowFrameClause { $args.push( $wf.wf ); }
|
|
2431
|
+
)?
|
|
2432
|
+
;
|
|
2433
|
+
|
|
2434
|
+
partitionByClause returns [ expr ] locals[ args = [] ]
|
|
2435
|
+
@after{ $expr = this.argsExpression( $args ); }
|
|
2436
|
+
:
|
|
2437
|
+
e1=expression { $args.push( $e1.expr ); }
|
|
2438
|
+
( ',' { this.pushXprToken( $args ); }
|
|
2439
|
+
en=expression { $args.push( $en.expr ); }
|
|
2440
|
+
)*
|
|
2441
|
+
;
|
|
2442
|
+
|
|
2443
|
+
// ORDER BY clause in generic functions, e.g. `first_value(id order by name)`
|
|
2444
|
+
funcOrderByClause[ args ] returns[ expr ]
|
|
2445
|
+
@after{ $expr = this.argsExpression( args, true ); }
|
|
2446
|
+
:
|
|
2447
|
+
ORDER { this.pushXprToken( $args ); } BY { this.pushXprToken( $args ); }
|
|
2448
|
+
ob=exprOrderByClause { $args.push( $ob.expr ); }
|
|
2449
|
+
;
|
|
2450
|
+
|
|
2451
|
+
// ORDER BY clause in generic functions or OVER clause
|
|
2452
|
+
exprOrderByClause returns[ expr ] locals[ args = [] ]
|
|
2453
|
+
@after{ $expr = this.argsExpression( $args ); }
|
|
2454
|
+
:
|
|
2455
|
+
orderBySpecInExpr[ $args ]
|
|
2456
|
+
( ',' { this.pushXprToken( $args ); } orderBySpecInExpr[ $args ] )*
|
|
2457
|
+
;
|
|
2458
|
+
|
|
2459
|
+
orderBySpecInExpr[ args ]
|
|
2460
|
+
:
|
|
2461
|
+
e=expression { $args.push( $e.expr ); }
|
|
2462
|
+
( ASC { this.pushXprToken( $args ); }
|
|
2463
|
+
| DESC { this.pushXprToken( $args ); }
|
|
2464
|
+
)?
|
|
2465
|
+
( NULLS { this.pushXprToken( $args ); }
|
|
2466
|
+
( FIRST | LAST ) { this.pushXprToken( $args ); }
|
|
2467
|
+
)?
|
|
2468
|
+
;
|
|
2469
|
+
|
|
2470
|
+
windowFrameClause returns[ wf ] locals[ args = [] ]
|
|
2471
|
+
@after{ $wf = this.argsExpression( $args ); }
|
|
2472
|
+
:
|
|
2473
|
+
(
|
|
2474
|
+
windowFrameStartSpec[ $args ]
|
|
2475
|
+
|
|
|
2476
|
+
BETWEEN { this.pushXprToken( $args ); }
|
|
2477
|
+
windowFrameBoundSpec[ $args ]
|
|
2478
|
+
AND { this.pushXprToken( $args ); }
|
|
2479
|
+
windowFrameBoundSpec[ $args ]
|
|
2480
|
+
)
|
|
2481
|
+
;
|
|
2482
|
+
|
|
2483
|
+
windowFrameBoundSpec[ args ]
|
|
2484
|
+
:
|
|
2485
|
+
UNBOUNDED { this.pushXprToken( $args ); }
|
|
2486
|
+
( FOLLOWING | PRECEDING ) { this.pushXprToken( $args ); }
|
|
2487
|
+
|
|
|
2488
|
+
n=Number { $args.push( this.numberLiteral( $n ) ); }
|
|
2489
|
+
( FOLLOWING | PRECEDING ) { this.pushXprToken( $args ); }
|
|
2490
|
+
|
|
|
2491
|
+
CURRENT { this.pushXprToken( $args ); } ROW { this.pushXprToken( $args ); }
|
|
2492
|
+
;
|
|
2493
|
+
|
|
2494
|
+
windowFrameStartSpec[ args = [] ]
|
|
2495
|
+
:
|
|
2496
|
+
UNBOUNDED { this.pushXprToken( $args ); }
|
|
2497
|
+
PRECEDING { this.pushXprToken( $args ); }
|
|
2498
|
+
|
|
|
2499
|
+
n=Number { $args.push( this.numberLiteral( $n ) ); }
|
|
2500
|
+
PRECEDING { this.pushXprToken( $args ); }
|
|
2501
|
+
|
|
|
2502
|
+
CURRENT { this.pushXprToken( $args ); } ROW { this.pushXprToken( $args ); }
|
|
2503
|
+
;
|
|
2504
|
+
|
|
2579
2505
|
cardinalityAndFilter[ pathStep ] locals [ _sync = 'nop' ]
|
|
2580
2506
|
:
|
|
2581
2507
|
'['
|
|
@@ -2607,13 +2533,171 @@ optionalWhereForFilter
|
|
|
2607
2533
|
// one of the expected ones.
|
|
2608
2534
|
{
|
|
2609
2535
|
var text = this.getCurrentToken().text.toUpperCase();
|
|
2610
|
-
if (!['WHERE','GROUP','ORDER','LIMIT'].includes( text )) return;
|
|
2536
|
+
if (!['WHERE','GROUP','ORDER','LIMIT'].includes( text )) return $ctx;
|
|
2611
2537
|
// TODO: should we somehow add those keywords to $(EXPECTED)?
|
|
2612
2538
|
}
|
|
2613
2539
|
WHERE
|
|
2614
2540
|
;
|
|
2615
2541
|
|
|
2616
|
-
//
|
|
2542
|
+
// Annotation assignments -------------------------------------------------------
|
|
2543
|
+
|
|
2544
|
+
// We have three versions of the annotation assignment rules:
|
|
2545
|
+
// - "fix": typically after a name if a ':' could follow
|
|
2546
|
+
// - "ll1": typically before keyword+name, after a name if no ':' could follow
|
|
2547
|
+
// - "atn": at the beginning of a column definition
|
|
2548
|
+
//
|
|
2549
|
+
// want to let the ambiguity in select items (solution: "either" possibility)
|
|
2550
|
+
//
|
|
2551
|
+
// entity E @Anno: Base { … }; // Base is include (chosen w/ warning) or @Anno value?
|
|
2552
|
+
// entity V(p) as select from E { // either: anno value "ref p", select item -x
|
|
2553
|
+
// @anno :p - x as x; // or: anno value true, select item :p-x
|
|
2554
|
+
// }
|
|
2555
|
+
|
|
2556
|
+
annotationAssignment_fix[ art ] locals[ assignment ]
|
|
2557
|
+
// value outside @(...)
|
|
2558
|
+
@after {
|
|
2559
|
+
if ($assignment) {
|
|
2560
|
+
this.assignAnnotation( $art, $assignment );
|
|
2561
|
+
this.docComment( $art );
|
|
2562
|
+
}
|
|
2563
|
+
} :
|
|
2564
|
+
'@'
|
|
2565
|
+
(
|
|
2566
|
+
annotationAssignment_paren[ $art ]
|
|
2567
|
+
|
|
|
2568
|
+
{ $assignment = { name: {} }; }
|
|
2569
|
+
annotationPath[ $assignment.name, 'anno' ]
|
|
2570
|
+
annotationPathVariant[ $assignment.name ]?
|
|
2571
|
+
{ this.warnIfColonFollows( $assignment ); }
|
|
2572
|
+
)
|
|
2573
|
+
;
|
|
2574
|
+
|
|
2575
|
+
annotationAssignment_ll1[ art ] locals[ assignment ]
|
|
2576
|
+
@after {
|
|
2577
|
+
if ($assignment) {
|
|
2578
|
+
this.assignAnnotation( $art, $assignment );
|
|
2579
|
+
this.docComment( $art );
|
|
2580
|
+
}
|
|
2581
|
+
} :
|
|
2582
|
+
'@'
|
|
2583
|
+
(
|
|
2584
|
+
annotationAssignment_paren[ $art ]
|
|
2585
|
+
|
|
|
2586
|
+
{ $assignment = { name: {} }; }
|
|
2587
|
+
annotationPath[ $assignment.name, 'anno' ]
|
|
2588
|
+
annotationPathVariant[ $assignment.name ]?
|
|
2589
|
+
(
|
|
2590
|
+
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
2591
|
+
val=annoValue[ $assignment ]
|
|
2592
|
+
)?
|
|
2593
|
+
)
|
|
2594
|
+
;
|
|
2595
|
+
|
|
2596
|
+
// Has previously used ATN, now via local token rewrite
|
|
2597
|
+
annotationAssignment_atn[ art ] locals[ assignment ]
|
|
2598
|
+
@after {
|
|
2599
|
+
if ($assignment) {
|
|
2600
|
+
this.assignAnnotation( $art, $assignment );
|
|
2601
|
+
this.docComment( $art );
|
|
2602
|
+
}
|
|
2603
|
+
} :
|
|
2604
|
+
'@'
|
|
2605
|
+
(
|
|
2606
|
+
annotationAssignment_paren[ $art ]
|
|
2607
|
+
|
|
|
2608
|
+
{ $assignment = { name: {} }; }
|
|
2609
|
+
annotationPath[ $assignment.name, 'anno' ]
|
|
2610
|
+
// '#' is in the follow set of this rule, as it is used in rule "selectItemDef"
|
|
2611
|
+
// before an "expression" which can start with a '#' for an enum value
|
|
2612
|
+
// -> used to introduce variant name if and only if in same line as previous token
|
|
2613
|
+
{ this.setLocalToken( '#', 'HelperToken1', null, true ); }
|
|
2614
|
+
(
|
|
2615
|
+
HelperToken1 { this.meltKeywordToIdentifier(); }
|
|
2616
|
+
variant=ident['variant'] { $assignment.name.variant = $variant.id; }
|
|
2617
|
+
)?
|
|
2618
|
+
// ':' is in the follow set of this rule, as it is used in rule "selectItemDef"
|
|
2619
|
+
// before an "expression" which can start with a ':' for a parameter reference
|
|
2620
|
+
// -> used to introduce assignment value if and only if in same line as previous token
|
|
2621
|
+
{ this.setLocalToken( ':', 'HelperToken2', null, true ); }
|
|
2622
|
+
( HelperToken2 // ':'
|
|
2623
|
+
{ this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
2624
|
+
(
|
|
2625
|
+
val=annoValueBase[ $assignment ]
|
|
2626
|
+
|
|
|
2627
|
+
at='@'? annotationPath[ $assignment, 'ref', $at ]
|
|
2628
|
+
{ this.setLocalToken( '#', 'HelperToken1', null, true ); } // see above
|
|
2629
|
+
(
|
|
2630
|
+
HelperToken1 { this.meltKeywordToIdentifier(); }
|
|
2631
|
+
variant=ident['variant'] { $assignment.variant = $variant.id; }
|
|
2632
|
+
)?
|
|
2633
|
+
)
|
|
2634
|
+
)?
|
|
2635
|
+
)
|
|
2636
|
+
;
|
|
2637
|
+
|
|
2638
|
+
annotationAssignment_paren[ art ]
|
|
2639
|
+
:
|
|
2640
|
+
'('
|
|
2641
|
+
// allow completely useless `@()` with a warning, do not offer it for completion
|
|
2642
|
+
{
|
|
2643
|
+
this.meltKeywordToIdentifier();
|
|
2644
|
+
if (this.isStraightBefore(')')) {
|
|
2645
|
+
// TODO: or should we simple accept this without warning? (but still no CC)
|
|
2646
|
+
this.warning( 'syntax-unexpected-right-paren',
|
|
2647
|
+
this.tokenLocation( this.getCurrentToken() ),
|
|
2648
|
+
{ offending: "')'", expecting: ['Identifier'], code: '@()' },
|
|
2649
|
+
'Unexpected $(OFFENDING), expecting $(EXPECTING); ignoring $(CODE)' );
|
|
2650
|
+
this.matchWildcard(); // we know it is the ')' - we do not reach the final match
|
|
2651
|
+
return $ctx;
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
annotationAssignment_1[ $art ]
|
|
2655
|
+
( ','
|
|
2656
|
+
{
|
|
2657
|
+
this.meltKeywordToIdentifier();
|
|
2658
|
+
if (this.isStraightBefore(')')) break; // allow ',' before ')'
|
|
2659
|
+
}
|
|
2660
|
+
annotationAssignment_1[ $art ]
|
|
2661
|
+
)*
|
|
2662
|
+
')'
|
|
2663
|
+
;
|
|
2664
|
+
|
|
2665
|
+
annotationAssignment_1[ art ] locals[ assignment = { name: {} } ]
|
|
2666
|
+
@after { this.assignAnnotation( $art, $assignment ); }
|
|
2667
|
+
:
|
|
2668
|
+
annotationPath[ $assignment.name, 'anno' ]
|
|
2669
|
+
annotationPathVariant[ $assignment.name ]?
|
|
2670
|
+
(
|
|
2671
|
+
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
2672
|
+
val=annoValue[ $assignment ]
|
|
2673
|
+
)?
|
|
2674
|
+
;
|
|
2675
|
+
|
|
2676
|
+
annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
|
|
2677
|
+
@after { this.attachLocation($art); }
|
|
2678
|
+
// Due to error recovery, rule `ident` can return with value `null`. Set the
|
|
2679
|
+
// path as broken in this case.
|
|
2680
|
+
:
|
|
2681
|
+
head=ident[ $category ]
|
|
2682
|
+
{ $art.path = []; this.pushIdent( $art.path, $head.id, $headat );
|
|
2683
|
+
if ($category === 'artref') $art.scope = 0;
|
|
2684
|
+
}
|
|
2685
|
+
(
|
|
2686
|
+
'.' at='@'? tail=ident[ $category ]
|
|
2687
|
+
{ this.pushIdent( $art.path, $tail.id, $at );
|
|
2688
|
+
// Otherwise, $at may continue to be set after one `.@anno` segment.
|
|
2689
|
+
$ctx.at = null;
|
|
2690
|
+
}
|
|
2691
|
+
)*
|
|
2692
|
+
;
|
|
2693
|
+
|
|
2694
|
+
annotationPathVariant[ art ] locals[ variant = {} ]
|
|
2695
|
+
@after { this.attachLocation($art); }
|
|
2696
|
+
:
|
|
2697
|
+
// TODO: warning for space after '#'
|
|
2698
|
+
'#' { this.meltKeywordToIdentifier(); }
|
|
2699
|
+
simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
|
|
2700
|
+
;
|
|
2617
2701
|
|
|
2618
2702
|
annoValue[ assignment ]
|
|
2619
2703
|
:
|
|
@@ -2742,6 +2826,8 @@ annoSubValue returns[ val = {} ]
|
|
|
2742
2826
|
( annotationPathVariant[ $val ] )?
|
|
2743
2827
|
;
|
|
2744
2828
|
|
|
2829
|
+
// Literal values and identifiers -----------------------------------------------
|
|
2830
|
+
|
|
2745
2831
|
literalValue returns[ val ] locals[ tok ]
|
|
2746
2832
|
@init{ $tok = this.getCurrentToken(); }
|
|
2747
2833
|
@after { this.attachLocation($val); }
|
|
@@ -2765,47 +2851,7 @@ literalValue returns[ val ] locals[ tok ]
|
|
|
2765
2851
|
{ $val = this.quotedLiteral( $tok ); }
|
|
2766
2852
|
;
|
|
2767
2853
|
|
|
2768
|
-
|
|
2769
|
-
@after { this.attachLocation($art); }
|
|
2770
|
-
// Due to error recovery, rule `ident` can return with value `null`. Set the
|
|
2771
|
-
// path as broken in this case.
|
|
2772
|
-
:
|
|
2773
|
-
head=ident[ $category ]
|
|
2774
|
-
{ if (!$art.path) $art.path = []; this.pushIdent( $art.path, $head.id );
|
|
2775
|
-
if ($category === 'artref') $art.scope = 0;
|
|
2776
|
-
}
|
|
2777
|
-
(
|
|
2778
|
-
'.' tail=ident[ $category ] { this.pushIdent( $art.path, $tail.id ); }
|
|
2779
|
-
)*
|
|
2780
|
-
;
|
|
2781
|
-
|
|
2782
|
-
annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
|
|
2783
|
-
@after { this.attachLocation($art); }
|
|
2784
|
-
// Due to error recovery, rule `ident` can return with value `null`. Set the
|
|
2785
|
-
// path as broken in this case.
|
|
2786
|
-
:
|
|
2787
|
-
head=ident[ $category ]
|
|
2788
|
-
{ $art.path = []; this.pushIdent( $art.path, $head.id, $headat );
|
|
2789
|
-
if ($category === 'artref') $art.scope = 0;
|
|
2790
|
-
}
|
|
2791
|
-
(
|
|
2792
|
-
'.' at='@'? tail=ident[ $category ]
|
|
2793
|
-
{ this.pushIdent( $art.path, $tail.id, $at );
|
|
2794
|
-
// Otherwise, $at may continue to be set after one `.@anno` segment.
|
|
2795
|
-
$ctx.at = null;
|
|
2796
|
-
}
|
|
2797
|
-
)*
|
|
2798
|
-
;
|
|
2799
|
-
|
|
2800
|
-
annotationPathVariant[ art ] locals[ variant = {} ]
|
|
2801
|
-
@after { this.attachLocation($art); }
|
|
2802
|
-
:
|
|
2803
|
-
// TODO: warning for space after '#'
|
|
2804
|
-
'#' { this.meltKeywordToIdentifier(); }
|
|
2805
|
-
simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
|
|
2806
|
-
;
|
|
2807
|
-
|
|
2808
|
-
// Identifier and non-reserved keywords --------------------------------------
|
|
2854
|
+
// #IDENT - keep this comment here, used in scripts/linter/lintGrammar.js
|
|
2809
2855
|
|
|
2810
2856
|
identNoKeyword[ category ] returns[ id ] // for aliases without AS
|
|
2811
2857
|
@after{ $id = this.identAst( $stop, $category ); }
|
|
@@ -2840,7 +2886,6 @@ ident[ category ] returns[ id ]
|
|
|
2840
2886
|
| DEFINE
|
|
2841
2887
|
| DEFINITIONS
|
|
2842
2888
|
| DESC
|
|
2843
|
-
| ELEMENT
|
|
2844
2889
|
| ELEMENTS
|
|
2845
2890
|
| ELSE
|
|
2846
2891
|
| END
|
|
@@ -2870,7 +2915,6 @@ ident[ category ] returns[ id ]
|
|
|
2870
2915
|
| LIMIT
|
|
2871
2916
|
| LOCALIZED
|
|
2872
2917
|
| MANY
|
|
2873
|
-
| MASKED
|
|
2874
2918
|
| MINUS
|
|
2875
2919
|
| MIXIN
|
|
2876
2920
|
| NAMESPACE
|
|
@@ -2901,7 +2945,7 @@ ident[ category ] returns[ id ]
|
|
|
2901
2945
|
| VIEW
|
|
2902
2946
|
;
|
|
2903
2947
|
|
|
2904
|
-
|
|
2948
|
+
// LEXER ------------------------------------------------------------------------
|
|
2905
2949
|
|
|
2906
2950
|
WhiteSpace // like \s in JavaScript RegExp
|
|
2907
2951
|
: // LineTerminator | [\t\f\v\u00A0\uFEFF] | Zs
|
|
@@ -2914,10 +2958,10 @@ Comment : '/*' .*? '*/' -> channel(HIDDEN);
|
|
|
2914
2958
|
|
|
2915
2959
|
LineComment : '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);
|
|
2916
2960
|
|
|
2917
|
-
//
|
|
2961
|
+
// Literal values ---------------------------------------------------------------
|
|
2918
2962
|
|
|
2919
2963
|
// for syntactic code-completion: Combine all three string styles
|
|
2920
|
-
// Note: Use rule `string` instead as that also parses escape sequences!
|
|
2964
|
+
// Note: Use rule `string` instead as that also parses escape sequences! (TODO: ???)
|
|
2921
2965
|
String : SingleLineString
|
|
2922
2966
|
| MultiLineString
|
|
2923
2967
|
| MutlLineStringBlock;
|
|
@@ -3037,7 +3081,7 @@ DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
|
|
|
3037
3081
|
DEFINE : [dD][eE][fF][iI][nN][eE] ;
|
|
3038
3082
|
DEFINITIONS : [dD][eE][fF][iI][nN][iI][tT][iI][oO][nN][sS] ;
|
|
3039
3083
|
DESC : [dD][eE][sS][cC] ;
|
|
3040
|
-
ELEMENT : [eE][lL][eE][mM][eE][nN][tT] ;
|
|
3084
|
+
// ELEMENT : [eE][lL][eE][mM][eE][nN][tT] ;
|
|
3041
3085
|
ELEMENTS : [eE][lL][eE][mM][eE][nN][tT][sS] ;
|
|
3042
3086
|
ELSE : [eE][lL][sS][eE] ;
|
|
3043
3087
|
END : [eE][nN][dD] ;
|
|
@@ -3067,7 +3111,7 @@ LIKE : [lL][iI][kK][eE] ;
|
|
|
3067
3111
|
LIMIT : [lL][iI][mM][iI][tT] ;
|
|
3068
3112
|
LOCALIZED: [lL][oO][cC][aA][lL][iI][zZ][eE][dD];
|
|
3069
3113
|
MANY : [mM][aA][nN][yY] ;
|
|
3070
|
-
MASKED : [mM][aA][sS][kK][eE][dD] ;
|
|
3114
|
+
// MASKED : [mM][aA][sS][kK][eE][dD] ;
|
|
3071
3115
|
MINUS : [mM][iI][nN][uU][sS] ;
|
|
3072
3116
|
MIXIN : [mM][iI][xX][iI][nN] ;
|
|
3073
3117
|
NAMESPACE : [nN][aA][mM][eE][sS][pP][aA][cC][eE] ;
|