@sap/cds-compiler 3.4.4 → 3.5.2
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 +58 -0
- package/README.md +1 -0
- package/bin/cds_update_identifiers.js +5 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +9 -1
- package/doc/CHANGELOG_DEPRECATED.md +2 -0
- package/lib/api/main.js +58 -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 +124 -28
- package/lib/base/messages.js +247 -179
- 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/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 +1 -1
- package/lib/compiler/assert-consistency.js +0 -1
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/checks.js +73 -15
- package/lib/compiler/define.js +3 -7
- package/lib/compiler/extend.js +212 -32
- package/lib/compiler/finalize-parse-cdl.js +7 -2
- package/lib/compiler/index.js +17 -14
- package/lib/compiler/populate.js +2 -5
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/shared.js +23 -12
- package/lib/compiler/tweak-assocs.js +5 -6
- package/lib/compiler/utils.js +6 -0
- package/lib/edm/annotations/genericTranslation.js +553 -319
- package/lib/edm/annotations/preprocessAnnotations.js +39 -35
- package/lib/edm/csn2edm.js +88 -75
- package/lib/edm/edm.js +17 -3
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmPreprocessor.js +106 -76
- package/lib/edm/edmUtils.js +41 -2
- package/lib/gen/Dictionary.json +34 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +66 -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 +14360 -14146
- 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 +82 -40
- package/lib/json/to-csn.js +82 -157
- package/lib/language/.eslintrc.json +1 -4
- package/lib/language/genericAntlrParser.js +59 -38
- package/lib/language/language.g4 +1508 -1490
- package/lib/language/multiLineStringParser.js +1 -1
- package/lib/main.js +3 -3
- package/lib/model/csnUtils.js +130 -122
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/model/sortViews.js +4 -6
- package/lib/modelCompare/utils/filter.js +4 -3
- package/lib/optionProcessor.js +5 -0
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +12 -12
- package/lib/render/toCdl.js +225 -159
- package/lib/render/toHdbcds.js +63 -63
- package/lib/render/toRename.js +5 -5
- package/lib/render/toSql.js +55 -65
- package/lib/render/utils/common.js +20 -37
- package/lib/render/utils/delta.js +3 -3
- package/lib/render/utils/sql.js +22 -6
- package/lib/render/utils/stringEscapes.js +3 -3
- 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/db.js +10 -10
- package/lib/transform/draft/odata.js +2 -2
- package/lib/transform/forOdataNew.js +12 -40
- package/lib/transform/forRelationalDB.js +17 -7
- package/lib/transform/localized.js +2 -2
- package/lib/transform/odata/toFinalBaseType.js +41 -27
- package/lib/transform/odata/typesExposure.js +106 -62
- package/lib/transform/parseExpr.js +209 -106
- package/lib/transform/transformUtilsNew.js +2 -2
- 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 -7
- 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/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,170 +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
|
-
// TODO: or should we simple accept this without warning? (but still no CC)
|
|
243
|
-
this.warning( 'syntax-unexpected-right-paren',
|
|
244
|
-
this.tokenLocation( this.getCurrentToken() ),
|
|
245
|
-
{ offending: "')'", expecting: ['Identifier'], code: '@()' },
|
|
246
|
-
'Unexpected $(OFFENDING), expecting $(EXPECTING); ignoring $(CODE)' );
|
|
247
|
-
this.matchWildcard(); // we know it is the ')' - we do not reach the final match
|
|
248
|
-
return $ctx;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
annotationAssignment_1[ $art ]
|
|
252
|
-
( ','
|
|
253
|
-
{
|
|
254
|
-
this.meltKeywordToIdentifier();
|
|
255
|
-
if (this.isStraightBefore(')')) break; // allow ',' before ')'
|
|
256
|
-
}
|
|
257
|
-
annotationAssignment_1[ $art ]
|
|
258
|
-
)*
|
|
259
|
-
')'
|
|
260
|
-
;
|
|
261
|
-
|
|
262
|
-
annotationAssignment_fix[ art ] locals[ assignment ]
|
|
263
|
-
// value outside @(...)
|
|
264
|
-
@after {
|
|
265
|
-
if ($assignment) {
|
|
266
|
-
this.assignAnnotation( $art, $assignment );
|
|
267
|
-
this.docComment( $art );
|
|
268
|
-
}
|
|
269
|
-
} :
|
|
270
|
-
'@'
|
|
271
|
-
(
|
|
272
|
-
annotationAssignment_paren[ $art ]
|
|
273
|
-
|
|
|
274
|
-
{ $assignment = { name: {} }; }
|
|
275
|
-
annotationPath[ $assignment.name, 'anno' ]
|
|
276
|
-
annotationPathVariant[ $assignment.name ]?
|
|
277
|
-
{ this.warnIfColonFollows( $assignment ); }
|
|
278
|
-
)
|
|
279
|
-
;
|
|
280
|
-
|
|
281
|
-
annotationAssignment_ll1[ art ] locals[ assignment ]
|
|
282
|
-
@after {
|
|
283
|
-
if ($assignment) {
|
|
284
|
-
this.assignAnnotation( $art, $assignment );
|
|
285
|
-
this.docComment( $art );
|
|
286
|
-
}
|
|
287
|
-
} :
|
|
288
|
-
'@'
|
|
289
|
-
(
|
|
290
|
-
annotationAssignment_paren[ $art ]
|
|
291
|
-
|
|
|
292
|
-
{ $assignment = { name: {} }; }
|
|
293
|
-
annotationPath[ $assignment.name, 'anno' ]
|
|
294
|
-
annotationPathVariant[ $assignment.name ]?
|
|
295
|
-
(
|
|
296
|
-
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
297
|
-
val=annoValue[ $assignment ]
|
|
298
|
-
)?
|
|
299
|
-
)
|
|
300
|
-
;
|
|
301
|
-
|
|
302
|
-
// Has previously used ATN, now via local token rewrite
|
|
303
|
-
annotationAssignment_atn[ art ] locals[ assignment ]
|
|
304
|
-
@after {
|
|
305
|
-
if ($assignment) {
|
|
306
|
-
this.assignAnnotation( $art, $assignment );
|
|
307
|
-
this.docComment( $art );
|
|
308
|
-
}
|
|
309
|
-
} :
|
|
310
|
-
'@'
|
|
311
|
-
(
|
|
312
|
-
annotationAssignment_paren[ $art ]
|
|
313
|
-
|
|
|
314
|
-
{ $assignment = { name: {} }; }
|
|
315
|
-
annotationPath[ $assignment.name, 'anno' ]
|
|
316
|
-
// '#' is in the follow set of this rule, as it is used in rule "selectItemDef"
|
|
317
|
-
// before an "expression" which can start with a '#' for an enum value
|
|
318
|
-
// -> used to introduce variant name if and only if in same line as previous token
|
|
319
|
-
{ this.setLocalToken( '#', 'HelperToken1', null, true ); }
|
|
320
|
-
(
|
|
321
|
-
HelperToken1 { this.meltKeywordToIdentifier(); }
|
|
322
|
-
variant=ident['variant'] { $assignment.name.variant = $variant.id; }
|
|
323
|
-
)?
|
|
324
|
-
// ':' is in the follow set of this rule, as it is used in rule "selectItemDef"
|
|
325
|
-
// before an "expression" which can start with a ':' for a parameter reference
|
|
326
|
-
// -> used to introduce assignment value if and only if in same line as previous token
|
|
327
|
-
{ this.setLocalToken( ':', 'HelperToken2', null, true ); }
|
|
328
|
-
( HelperToken2 // ':'
|
|
329
|
-
{ this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
330
|
-
(
|
|
331
|
-
val=annoValueBase[ $assignment ]
|
|
332
|
-
|
|
|
333
|
-
at='@'? annotationPath[ $assignment, 'ref', $at ]
|
|
334
|
-
{ this.setLocalToken( '#', 'HelperToken1', null, true ); } // see above
|
|
335
|
-
(
|
|
336
|
-
HelperToken1 { this.meltKeywordToIdentifier(); }
|
|
337
|
-
variant=ident['variant'] { $assignment.variant = $variant.id; }
|
|
338
|
-
)?
|
|
339
|
-
)
|
|
340
|
-
)?
|
|
341
|
-
)
|
|
342
|
-
;
|
|
343
|
-
|
|
344
|
-
// Main artifact definitions -------------------------------------------------
|
|
345
|
-
|
|
346
|
-
requiredSemi
|
|
347
|
-
: ';'
|
|
348
|
-
| { return $ctx; } // do not actually parse the closing brace
|
|
349
|
-
'}'
|
|
350
|
-
;
|
|
351
|
-
|
|
352
|
-
optionalSemi
|
|
353
|
-
: { this.noAssignmentInSameLine(); } // issue warning for } @Anno \n? NextDef
|
|
354
|
-
';'?
|
|
355
|
-
;
|
|
356
|
-
|
|
357
|
-
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!
|
|
358
216
|
@after{ /* #ATN 1 */ }
|
|
359
217
|
:
|
|
360
218
|
{ $art.location = this.startLocation(); this.docComment( $art ); }
|
|
361
219
|
annotationAssignment_ll1[ $art ]*
|
|
362
220
|
(
|
|
363
221
|
DEFINE?
|
|
364
|
-
(
|
|
222
|
+
( serviceDef[ $art, $outer, defOnly ]
|
|
223
|
+
| contextDef[ $art, $outer, defOnly ]
|
|
365
224
|
| entityDef[ $art, $outer ]
|
|
366
225
|
| typeDef[ $art, $outer ]
|
|
367
226
|
| aspectDef[ $art, $outer ]
|
|
@@ -372,26 +231,27 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
|
|
|
372
231
|
)
|
|
373
232
|
|
|
|
374
233
|
extend=EXTEND
|
|
375
|
-
{ if (defOnly)
|
|
234
|
+
{ if (defOnly) // TODO: nicer message text
|
|
376
235
|
this.error( 'syntax-extend-context', $extend,
|
|
377
|
-
{ code: 'EXTEND artifact',
|
|
378
|
-
'No $(CODE) within $(
|
|
236
|
+
{ code: 'EXTEND artifact', meta: defOnly },
|
|
237
|
+
'No $(CODE) within $(META) extensions' );
|
|
379
238
|
if (!$outer.extensions) $outer.extensions = [];
|
|
380
239
|
}
|
|
381
|
-
// #ATN: EXTEND
|
|
382
|
-
(
|
|
383
|
-
|
|
|
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
|
|
384
244
|
| extendProjection[ $art, $outer ]
|
|
385
245
|
| extendType[ $art, $outer ]
|
|
386
|
-
// Streamlined Syntax
|
|
246
|
+
// Streamlined Syntax; we won't add more kinds of the non-streamlined variants:
|
|
387
247
|
| extendArtifact[ $art, $outer ]
|
|
388
248
|
)
|
|
389
249
|
|
|
|
390
250
|
annotate=ANNOTATE
|
|
391
|
-
{ if (defOnly)
|
|
251
|
+
{ if (defOnly) // TODO: improve message text
|
|
392
252
|
this.error( 'syntax-extend-context', $annotate,
|
|
393
|
-
{ code: 'ANNOTATE artifact',
|
|
394
|
-
'No $(CODE) within $(
|
|
253
|
+
{ code: 'ANNOTATE artifact', meta: defOnly },
|
|
254
|
+
'No $(CODE) within $(META) extensions' );
|
|
395
255
|
if (!$outer.extensions) $outer.extensions = [];
|
|
396
256
|
this.meltKeywordToIdentifier();
|
|
397
257
|
}
|
|
@@ -399,16 +259,14 @@ artifactDef[ outer, defOnly = false ] locals[ art = {} ] // cannot use `parent`
|
|
|
399
259
|
)
|
|
400
260
|
;
|
|
401
261
|
|
|
402
|
-
|
|
262
|
+
optArtifactsBlock[ art, defOnly = false ]
|
|
403
263
|
@after { this.attachLocation( $art ); }
|
|
404
264
|
:
|
|
405
|
-
(
|
|
406
|
-
{ this.addDef( $art, $outer, 'artifacts', $service ? 'service' : 'context', $name );
|
|
407
|
-
this.docComment( $art ); }
|
|
265
|
+
{ this.docComment( $art ); }
|
|
408
266
|
annotationAssignment_fix[ $art ]*
|
|
409
267
|
(
|
|
410
268
|
'{' { $art.artifacts = this.createDict(); $art.extensions = []; }
|
|
411
|
-
|
|
269
|
+
artifactDefOrExtend[ $art, defOnly ]*
|
|
412
270
|
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
413
271
|
optionalSemi
|
|
414
272
|
|
|
|
@@ -416,25 +274,110 @@ contextDef[ art, outer, defOnly = false ] locals[ name = {} ]
|
|
|
416
274
|
)
|
|
417
275
|
;
|
|
418
276
|
|
|
419
|
-
|
|
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 = {} ]
|
|
420
291
|
@after { this.attachLocation( $art ); }
|
|
421
292
|
:
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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 ]*
|
|
428
329
|
(
|
|
429
|
-
|
|
430
|
-
artifactDef[ $art, $service ? 'SERVICE' : 'CONTEXT' ]*
|
|
431
|
-
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
432
|
-
optionalSemi
|
|
330
|
+
typeStruct[ $art ] optionalSemi
|
|
433
331
|
|
|
|
434
|
-
|
|
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
|
+
)
|
|
435
355
|
)
|
|
436
356
|
;
|
|
437
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
|
+
|
|
438
381
|
entityDef[ art, outer ] locals[ name = {} ]
|
|
439
382
|
@after { this.attachLocation( $art ); }
|
|
440
383
|
:
|
|
@@ -453,7 +396,6 @@ entityDef[ art, outer ] locals[ name = {} ]
|
|
|
453
396
|
'{' { $art.elements = this.createDict(); }
|
|
454
397
|
elementDef[ $art ]*
|
|
455
398
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
456
|
-
// TODO: action definitions in a specific section?
|
|
457
399
|
(
|
|
458
400
|
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
459
401
|
actionFunctionDef[ $art ]*
|
|
@@ -484,161 +426,48 @@ entityDef[ art, outer ] locals[ name = {} ]
|
|
|
484
426
|
)
|
|
485
427
|
;
|
|
486
428
|
|
|
487
|
-
|
|
488
|
-
@after { this.attachLocation($query); }
|
|
489
|
-
:
|
|
490
|
-
proj=PROJECTION ON // FIXME: First draft only, details unclear/unspecified
|
|
491
|
-
// now a simplified `tableTerm`:
|
|
492
|
-
{
|
|
493
|
-
$src = { path: [], scope: 0 };
|
|
494
|
-
$query = { op: this.valueWithTokenLocation( 'SELECT', $proj ), from: $src, location: this.startLocation() };
|
|
495
|
-
}
|
|
496
|
-
fromPath[ $src, 'artref']
|
|
497
|
-
( ':'
|
|
498
|
-
{ $src.scope = $src.path.length; }
|
|
499
|
-
fromPath[ $src, 'ref']
|
|
500
|
-
)?
|
|
501
|
-
( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
|
|
502
|
-
// ANTLR errors are better if we use ( A )? instead of ( A | ):
|
|
503
|
-
{ if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
|
|
504
|
-
bracedSelectItemListDef[ $query, 'columns' ]?
|
|
505
|
-
excludingClause[ $query ]?
|
|
506
|
-
;
|
|
507
|
-
|
|
508
|
-
projectionClauses[ query ]
|
|
509
|
-
@after { this.attachLocation($query); }
|
|
510
|
-
:
|
|
511
|
-
( WHERE cond=condition { $query.where = $cond.cond; } )?
|
|
512
|
-
(
|
|
513
|
-
GROUP BY
|
|
514
|
-
e1=expression { $query.groupBy = [ $e1.expr ]; }
|
|
515
|
-
( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
|
|
516
|
-
)?
|
|
517
|
-
( HAVING having=condition { $query.having = $having.cond; } )?
|
|
518
|
-
( ob=orderByClause[ $query ] { $query = $ob.query; } ) ?
|
|
519
|
-
( lc=limitClause[ $query ] { $query = $lc.query; } ) ?
|
|
520
|
-
;
|
|
521
|
-
|
|
522
|
-
excludingClause[ query ]
|
|
523
|
-
:
|
|
524
|
-
// syntax is less than ideal - EXCLUDING is only useful for `*` - with
|
|
525
|
-
// this syntax, people wonder what happens with explicit select items
|
|
526
|
-
EXCLUDING '{' { $query.excludingDict = this.createDict(); }
|
|
527
|
-
projectionExclusion[ $query ]
|
|
528
|
-
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
529
|
-
projectionExclusion[ $query ]
|
|
530
|
-
)*
|
|
531
|
-
'}' { this.finalizeDictOrArray( $query.excludingDict ); }
|
|
532
|
-
;
|
|
533
|
-
|
|
534
|
-
projectionExclusion[ outer ] locals[ art = {} ]
|
|
535
|
-
@after { this.attachLocation($art); }
|
|
536
|
-
:
|
|
537
|
-
name=ident['ref']
|
|
538
|
-
{ this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
|
|
539
|
-
;
|
|
540
|
-
|
|
541
|
-
// also used for aspect
|
|
542
|
-
extendEntity[ art, outer ] locals[ name = {} ]
|
|
543
|
-
@after { /* #ATN 1 */ this.attachLocation( $art ); }
|
|
544
|
-
:
|
|
545
|
-
kind=(ASPECT | ENTITY) simplePath[ $name, 'Extend' ]
|
|
546
|
-
{ $art.expectedKind = $kind.text.toLowerCase(); $art.name = $name;
|
|
547
|
-
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
548
|
-
}
|
|
549
|
-
(
|
|
550
|
-
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
551
|
-
annotationAssignment_ll1[ $art ]*
|
|
552
|
-
// ATN: the ref can start with ACTIONS
|
|
553
|
-
(
|
|
554
|
-
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
555
|
-
requiredSemi
|
|
556
|
-
|
|
|
557
|
-
extendForEntity[ $art ]
|
|
558
|
-
)
|
|
559
|
-
|
|
|
560
|
-
{ this.docComment( $art ); }
|
|
561
|
-
annotationAssignment_ll1[ $art ]*
|
|
562
|
-
extendForEntity[ $art ]
|
|
563
|
-
)
|
|
564
|
-
;
|
|
565
|
-
|
|
566
|
-
extendForEntity[ art ]
|
|
567
|
-
:
|
|
568
|
-
'{' { $art.elements = this.createDict(); }
|
|
569
|
-
elementDefOrExtend[ $art ]*
|
|
570
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
571
|
-
(
|
|
572
|
-
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
573
|
-
actionFunctionDef[ $art ]*
|
|
574
|
-
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
575
|
-
)?
|
|
576
|
-
optionalSemi
|
|
577
|
-
|
|
|
578
|
-
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
579
|
-
actionFunctionDef[ $art ]*
|
|
580
|
-
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
581
|
-
optionalSemi
|
|
582
|
-
|
|
|
583
|
-
requiredSemi
|
|
584
|
-
;
|
|
585
|
-
|
|
586
|
-
extendProjection[ art, outer ] locals[ name = {} ]
|
|
429
|
+
aspectDef[ art, outer ] locals[ name = {} ]
|
|
587
430
|
@after { this.attachLocation( $art ); }
|
|
588
431
|
:
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
(
|
|
597
|
-
'{' { $art.columns = []; }
|
|
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 ]*
|
|
439
|
+
( ':'
|
|
598
440
|
(
|
|
599
|
-
|
|
600
|
-
( ',' { if (this.isStraightBefore(
|
|
601
|
-
|
|
441
|
+
includeRef[ $art ]
|
|
442
|
+
( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
|
|
443
|
+
includeRef[ $art ]
|
|
602
444
|
)*
|
|
603
445
|
)?
|
|
604
|
-
|
|
446
|
+
)?
|
|
447
|
+
( // `aspect MyAspect {};`
|
|
448
|
+
'{' { $art.elements = this.createDict(); }
|
|
449
|
+
( elementDef[ $art ]* )
|
|
450
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
605
451
|
(
|
|
606
452
|
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
607
453
|
actionFunctionDef[ $art ]*
|
|
608
454
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
609
455
|
)?
|
|
610
456
|
optionalSemi
|
|
611
|
-
|
|
|
612
|
-
|
|
613
|
-
actionFunctionDef[ $art ]*
|
|
614
|
-
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
615
|
-
optionalSemi
|
|
616
|
-
|
|
|
457
|
+
| // `aspect MyAspect;`, e.g. for annotation aspects.
|
|
458
|
+
{ this.aspectWithoutElements( $art ); }
|
|
617
459
|
requiredSemi
|
|
618
460
|
)
|
|
619
461
|
;
|
|
620
462
|
|
|
621
|
-
|
|
622
|
-
actionFunctionDef[ outer ] locals[ art = {} ]
|
|
463
|
+
typeDef[ art, outer ] locals[ name = {} ]
|
|
623
464
|
@after { this.attachLocation( $art ); }
|
|
624
465
|
:
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
this.docComment( $art ); }
|
|
631
|
-
annotationAssignment_fix[ $art ]*
|
|
632
|
-
parameterListDef[ $art ]
|
|
633
|
-
( returnTypeSpec[ $art ] | requiredSemi )
|
|
634
|
-
|
|
|
635
|
-
FUNCTION name=ident['BoundAction']
|
|
636
|
-
{ this.addDef( $art, $outer, 'actions', 'function', $name.id );
|
|
637
|
-
this.docComment( $art ); }
|
|
638
|
-
annotationAssignment_fix[ $art ]*
|
|
639
|
-
parameterListDef[ $art ]
|
|
640
|
-
returnTypeSpec[ $art ]
|
|
641
|
-
)
|
|
466
|
+
TYPE simplePath[ $name, 'Type' ]
|
|
467
|
+
{ this.addDef( $art, $outer, 'artifacts', 'type', $name );
|
|
468
|
+
this.docComment( $art ); }
|
|
469
|
+
annotationAssignment_fix[ $art ]*
|
|
470
|
+
typeSpecSemi[ $art ]
|
|
642
471
|
;
|
|
643
472
|
|
|
644
473
|
actionFunctionMainDef[ art, outer ] locals[ name = {} ]
|
|
@@ -659,137 +488,254 @@ actionFunctionMainDef[ art, outer ] locals[ name = {} ]
|
|
|
659
488
|
returnTypeSpec[ $art ]
|
|
660
489
|
;
|
|
661
490
|
|
|
662
|
-
|
|
663
|
-
|
|
491
|
+
// Member definitions: actions, elements, enums, parameters: --------------------
|
|
492
|
+
|
|
493
|
+
actionFunctionDef[ outer ] locals[ art = {} ]
|
|
494
|
+
@after { this.attachLocation( $art ); }
|
|
664
495
|
:
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
this.docComment( $art ); }
|
|
668
|
-
annotationAssignment_fix[ $art ]*
|
|
496
|
+
{ $art.location = this.startLocation();; this.docComment( $art ); }
|
|
497
|
+
annotationAssignment_ll1[ $art ]*
|
|
669
498
|
(
|
|
670
|
-
|
|
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 )
|
|
671
505
|
|
|
|
672
|
-
'
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
{ $art.includes = [ $art.type ]; delete $art.type; }
|
|
679
|
-
( ',' { if (this.isStraightBefore('{')) break; } // allow ',' before '{' // }}
|
|
680
|
-
includeRef[ $art ]
|
|
681
|
-
)*
|
|
682
|
-
typeStruct[ $art ] optionalSemi
|
|
683
|
-
|
|
|
684
|
-
{ this.docComment( $art ); }
|
|
685
|
-
annotationAssignment_ll1[ $art ]*
|
|
686
|
-
requiredSemi
|
|
687
|
-
)
|
|
688
|
-
|
|
|
689
|
-
typeStruct[ $art ] optionalSemi
|
|
690
|
-
|
|
|
691
|
-
qp=projectionSpec
|
|
692
|
-
{ $art.query = $qp.query; $art['$'+'syntax'] = 'projection'; }
|
|
693
|
-
optionalSemi // TODO: not fully correct without columns or excluding
|
|
694
|
-
|
|
695
|
-
)
|
|
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 ]
|
|
696
512
|
)
|
|
697
513
|
;
|
|
698
514
|
|
|
699
|
-
|
|
515
|
+
parameterDef[ outer ] locals[ art = {} ]
|
|
700
516
|
@after { this.attachLocation( $art ); }
|
|
701
517
|
:
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
if ($ent)
|
|
707
|
-
this.warning( 'syntax-deprecated-abstract', this.tokenLocation( $abs, $ent ) );
|
|
518
|
+
{ this.docComment( $art ); }
|
|
519
|
+
annotationAssignment_ll1[ $art ]*
|
|
520
|
+
name=ident['Param']
|
|
521
|
+
{ this.addDef( $art, $outer, 'params', 'param', $name.id );
|
|
708
522
|
this.docComment( $art ); }
|
|
709
523
|
annotationAssignment_fix[ $art ]*
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
524
|
+
typeSpec[ $art ]
|
|
525
|
+
defaultValue[ $art ]?
|
|
526
|
+
{ this.docComment( $art ); }
|
|
527
|
+
annotationAssignment_ll1[ $art ]*
|
|
528
|
+
;
|
|
529
|
+
|
|
530
|
+
parameterListDef[ art ]
|
|
531
|
+
:
|
|
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
|
+
)*
|
|
717
540
|
)?
|
|
718
|
-
(
|
|
719
|
-
'{' { $art.elements = this.createDict(); }
|
|
720
|
-
( elementDef[ $art ]* )
|
|
721
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
722
|
-
// TODO: action definitions in a specific section?
|
|
723
|
-
(
|
|
724
|
-
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
725
|
-
actionFunctionDef[ $art ]*
|
|
726
|
-
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
727
|
-
)?
|
|
728
|
-
optionalSemi
|
|
729
|
-
| // `aspect MyAspect;`, e.g. for annotation aspects.
|
|
730
|
-
{ this.aspectWithoutElements( $art ); }
|
|
731
|
-
requiredSemi
|
|
732
|
-
)
|
|
541
|
+
')' { this.finalizeDictOrArray( $art.params ); }
|
|
733
542
|
;
|
|
734
543
|
|
|
735
|
-
|
|
544
|
+
enumSymbolDef[ outer ] locals[ art = {} ]
|
|
736
545
|
@after { this.attachLocation( $art ); }
|
|
737
546
|
:
|
|
738
|
-
|
|
739
|
-
|
|
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
|
|
566
|
+
;
|
|
567
|
+
|
|
568
|
+
elementDef[ outer ] locals[ $art = {} ]
|
|
569
|
+
:
|
|
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' } );
|
|
587
|
+
}
|
|
588
|
+
)?
|
|
589
|
+
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^[:{@=}]$/ ); }
|
|
590
|
+
ELEMENT?
|
|
591
|
+
name=ident['Element']
|
|
592
|
+
{ this.addDef( $art, $outer, 'elements', 'element', $name.id );
|
|
740
593
|
this.docComment( $art ); }
|
|
741
594
|
annotationAssignment_fix[ $art ]*
|
|
742
|
-
|
|
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
|
+
)
|
|
743
619
|
;
|
|
744
620
|
|
|
745
|
-
|
|
746
|
-
@after
|
|
621
|
+
elementType[ art ] // TODO: split this monster rule
|
|
622
|
+
@after{ /* #ATN 3 */ this.attachLocation( $art ); }
|
|
747
623
|
:
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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!
|
|
753
631
|
(
|
|
754
|
-
|
|
632
|
+
typeStruct[ $art.target, true ] optionalSemi
|
|
755
633
|
|
|
|
756
|
-
|
|
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 ); }
|
|
757
662
|
annotationAssignment_ll1[ $art ]*
|
|
758
663
|
(
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
requiredSemi
|
|
770
|
-
|
|
|
771
|
-
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
772
|
-
requiredSemi
|
|
773
|
-
)
|
|
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
|
+
)?
|
|
774
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
|
|
775
716
|
;
|
|
776
717
|
|
|
777
|
-
|
|
778
|
-
@after { this.attachLocation( $art ); }
|
|
718
|
+
elementProperties[ elem ]
|
|
779
719
|
:
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
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; }
|
|
791
735
|
;
|
|
792
736
|
|
|
737
|
+
// Extend and annotate ----------------------------------------------------------
|
|
738
|
+
|
|
793
739
|
extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
794
740
|
@after{ /* #ATN 1 */ this.attachLocation( $art ); }
|
|
795
741
|
:
|
|
@@ -830,6 +776,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
830
776
|
|
|
|
831
777
|
// extend Art with (length: 10);
|
|
832
778
|
// `with` is required, or we could have `extend String(length:10);`.
|
|
779
|
+
// future `extend Action with (param: Type)` now has ambiguity
|
|
833
780
|
typeNamedArgList[ $art ]
|
|
834
781
|
requiredSemi
|
|
835
782
|
|
|
|
@@ -839,7 +786,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
839
786
|
|
|
|
840
787
|
DEFINITIONS
|
|
841
788
|
'{' { $art.artifacts = this.createDict(); }
|
|
842
|
-
|
|
789
|
+
artifactDefOrExtend[ $art, true ]*
|
|
843
790
|
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
844
791
|
optionalSemi
|
|
845
792
|
|
|
|
@@ -859,71 +806,229 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
859
806
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
860
807
|
optionalSemi
|
|
861
808
|
)
|
|
809
|
+
// TODO: what about adding both ELEMENTS and ACTIONS? (TODO: csn input test & to-cdl)
|
|
862
810
|
)
|
|
863
811
|
;
|
|
864
812
|
|
|
865
|
-
|
|
813
|
+
extendService[ art, outer ] locals[ name = {} ]
|
|
814
|
+
@after { this.attachLocation( $art ); }
|
|
866
815
|
:
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
'{' { $art.elements = this.createDict(); }
|
|
873
|
-
elementDefOrExtend[ $art ]*
|
|
874
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
875
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
876
|
-
optionalSemi
|
|
877
|
-
|
|
|
878
|
-
ELEMENTS '{' { $art.elements = this.createDict(); }
|
|
879
|
-
elementDefOrExtend[ $art ]*
|
|
880
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
881
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
882
|
-
optionalSemi
|
|
883
|
-
|
|
|
884
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
885
|
-
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
886
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
887
|
-
optionalSemi
|
|
888
|
-
|
|
|
889
|
-
// extend type|element Art with (length: 10);
|
|
890
|
-
typeNamedArgList[ $art ]
|
|
891
|
-
requiredSemi
|
|
892
|
-
|
|
|
893
|
-
requiredSemi
|
|
894
|
-
)
|
|
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' ]
|
|
895
821
|
;
|
|
896
822
|
|
|
897
|
-
|
|
823
|
+
extendContext[ art, outer ] locals[ name = {} ]
|
|
824
|
+
@after { this.attachLocation( $art ); }
|
|
898
825
|
:
|
|
899
|
-
{
|
|
900
|
-
|
|
826
|
+
CONTEXT { $art.expectedKind = 'context'; }
|
|
827
|
+
simplePath[ $name, 'Context' ]
|
|
828
|
+
{ $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
|
|
829
|
+
( WITH { this.noSemicolonHere(); } )?
|
|
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
|
+
}
|
|
901
840
|
(
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
841
|
+
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
842
|
+
annotationAssignment_ll1[ $art ]*
|
|
843
|
+
// ATN: the ref can start with ACTIONS
|
|
844
|
+
(
|
|
845
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
846
|
+
requiredSemi
|
|
847
|
+
|
|
|
848
|
+
extendForEntity[ $art ]
|
|
849
|
+
)
|
|
907
850
|
|
|
|
908
|
-
|
|
851
|
+
{ this.docComment( $art ); }
|
|
852
|
+
annotationAssignment_ll1[ $art ]*
|
|
853
|
+
extendForEntity[ $art ]
|
|
909
854
|
)
|
|
910
855
|
;
|
|
911
856
|
|
|
912
|
-
|
|
913
|
-
@after { this.attachLocation( $art ); }
|
|
857
|
+
extendForEntity[ art ]
|
|
914
858
|
:
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
{ this.
|
|
918
|
-
( WITH { this.noSemicolonHere(); } )?
|
|
919
|
-
{ this.docComment( $art ); }
|
|
920
|
-
annotationAssignment_ll1[ $art ]*
|
|
859
|
+
'{' { $art.elements = this.createDict(); }
|
|
860
|
+
elementDefOrExtend[ $art ]*
|
|
861
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
921
862
|
(
|
|
922
|
-
'{' { $art.
|
|
923
|
-
|
|
924
|
-
'}' { this.finalizeDictOrArray( $art.
|
|
925
|
-
|
|
926
|
-
|
|
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
|
+
(
|
|
927
1032
|
ACTIONS
|
|
928
1033
|
'{' { $art.actions = this.createDict(); }
|
|
929
1034
|
annotateAction[ $art ]*
|
|
@@ -1029,482 +1134,68 @@ annotateParam [ outer ] locals [ art = {} ]
|
|
|
1029
1134
|
annotationAssignment_ll1[ $art ]*
|
|
1030
1135
|
;
|
|
1031
1136
|
|
|
1032
|
-
//
|
|
1137
|
+
// Type expressions -------------------------------------------------------------
|
|
1033
1138
|
|
|
1034
|
-
|
|
1035
|
-
@after { this.attachLocation( $art ); }
|
|
1139
|
+
includeRef[ art ] locals[ incl = {} ]
|
|
1036
1140
|
:
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
name=ident['Enum']
|
|
1040
|
-
{ this.addDef( $art, $outer, 'enum', 'enum', $name.id );
|
|
1041
|
-
this.docComment( $art ); }
|
|
1042
|
-
annotationAssignment_ll1[ $art ]*
|
|
1043
|
-
( '='
|
|
1044
|
-
{ this.excludeExpected( ['Boolean', 'QuotedLiteral', "'#'", 'NULL'] ); }
|
|
1045
|
-
(
|
|
1046
|
-
val=literalValue
|
|
1047
|
-
{ $art.value = $val.val; }
|
|
1048
|
-
|
|
|
1049
|
-
( plus='+' | min='-' ) num=Number
|
|
1050
|
-
{ $art.value = this.numberLiteral( $num, $plus||$min ); }
|
|
1051
|
-
)
|
|
1052
|
-
{ this.docComment( $art ); }
|
|
1053
|
-
annotationAssignment_ll1[ $art ]*
|
|
1054
|
-
)?
|
|
1055
|
-
requiredSemi
|
|
1141
|
+
simplePath[ $incl, 'artref' ]
|
|
1142
|
+
{ if ($art.includes) $art.includes.push($incl); else $art.includes = [$incl]; }
|
|
1056
1143
|
;
|
|
1057
1144
|
|
|
1058
|
-
|
|
1145
|
+
typeSpec[ art ] // for parameterDef
|
|
1146
|
+
@after{ /* #ATN 1 */ }
|
|
1059
1147
|
:
|
|
1060
|
-
|
|
1061
|
-
|
|
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
|
+
)?
|
|
1170
|
+
)
|
|
1062
1171
|
;
|
|
1063
1172
|
|
|
1064
|
-
|
|
1065
|
-
@after
|
|
1173
|
+
returnTypeSpec[ art ]
|
|
1174
|
+
@after{ /* #ATN 1 */ }
|
|
1066
1175
|
:
|
|
1067
|
-
{ $art.
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1176
|
+
ret=RETURNS { $art.returns = { location: this.tokenLocation( $ret ), kind: 'param' }; }
|
|
1177
|
+
// #ATN: typeSimple can start with ARRAY or TYPE
|
|
1178
|
+
( typeStruct[ $art.returns ]
|
|
1179
|
+
nullability[ $art.returns ]?
|
|
1180
|
+
| typeArray[ $art.returns ] // nullability is set in typeArray
|
|
1181
|
+
| typeTypeOf[ $art.returns ]
|
|
1182
|
+
nullability[ $art.returns ]?
|
|
1183
|
+
(
|
|
1184
|
+
ENUM '{' { $art.returns.enum = this.createDict(); }
|
|
1185
|
+
enumSymbolDef[ $art.returns ]*
|
|
1186
|
+
'}' { this.finalizeDictOrArray( $art.returns.enum ); }
|
|
1187
|
+
)?
|
|
1188
|
+
// TODO: no LOCALIZED ?
|
|
1189
|
+
| typeRefOptArgs[ $art.returns ]
|
|
1190
|
+
nullability[ $art.returns ]?
|
|
1191
|
+
(
|
|
1192
|
+
ENUM '{' { $art.returns.enum = this.createDict(); }
|
|
1193
|
+
enumSymbolDef[ $art.returns ]*
|
|
1194
|
+
'}' { this.finalizeDictOrArray( $art.returns.enum ); }
|
|
1195
|
+
)?
|
|
1075
1196
|
)
|
|
1076
|
-
;
|
|
1077
1197
|
|
|
1078
|
-
|
|
1079
|
-
:
|
|
1080
|
-
{ $art.location = this.startLocation();; this.docComment( $art ); }
|
|
1081
|
-
annotationAssignment_ll1[ $art ]*
|
|
1082
|
-
elementDefInner[ $art, $outer, false ]
|
|
1083
|
-
;
|
|
1084
|
-
|
|
1085
|
-
// Actually, this is a subset if elementDefInner...
|
|
1086
|
-
// TODO: the corresponding restrictions must also be checked in the core
|
|
1087
|
-
// compiler, as the mixin element could come via CSN
|
|
1088
|
-
mixinElementDef[ outer ] locals[ art = {} ]
|
|
1089
|
-
@after { /* #ATN 2 */ this.attachLocation($art); }
|
|
1090
|
-
:
|
|
1091
|
-
name=ident['Mixin']
|
|
1092
|
-
{ this.addDef( $art, $outer, 'mixin', 'mixin', $name.id ); }
|
|
1093
|
-
(
|
|
1094
|
-
':'
|
|
1095
|
-
// #ATN: referenced type name can be ASSOCIATION or COMPOSITION
|
|
1096
|
-
(
|
|
1097
|
-
typeAssociationBase[ $art, false ]
|
|
1098
|
-
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1099
|
-
( typeToMany[ $art ] | typeToOne[ $art ] | simplePath[ $art.target, 'artref' ] )
|
|
1100
|
-
typeAssociationCont[ $art ]?
|
|
1101
|
-
|
|
|
1102
|
-
typeRefOptArgs[ $art ]
|
|
1103
|
-
( as='=' expression
|
|
1104
|
-
{ this.error( 'syntax-unsupported-calc-field', $as ); }
|
|
1105
|
-
)?
|
|
1106
|
-
)
|
|
1107
|
-
|
|
|
1108
|
-
as='=' expression
|
|
1109
|
-
{ this.error( 'syntax-unsupported-calc-field', $as ); }
|
|
1110
|
-
)
|
|
1111
|
-
requiredSemi
|
|
1112
|
-
;
|
|
1113
|
-
|
|
1114
|
-
elementDefInner[ art, outer, allowEq ]
|
|
1115
|
-
@after{ /* #ATN 5 */ this.attachLocation( $art ); }
|
|
1116
|
-
:
|
|
1117
|
-
// TODO: it would be excellent to remove ELEMENT...
|
|
1118
|
-
// or have a special ident rule without the ELEMENT
|
|
1119
|
-
// Reason: it would be good for error recovery to start a major block without LL1 ambiguity
|
|
1120
|
-
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
1121
|
-
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[:{@=}]$/ ); }
|
|
1122
|
-
( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
|
|
1123
|
-
( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
|
|
1124
|
-
// #ATN: element name can be MASKED or ELEMENT (2x)
|
|
1125
|
-
( masked=MASKED
|
|
1126
|
-
{
|
|
1127
|
-
$art.masked = this.valueWithTokenLocation( true, $masked ) ;
|
|
1128
|
-
this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
|
|
1129
|
-
}
|
|
1130
|
-
)?
|
|
1131
|
-
// TODO: order?
|
|
1132
|
-
ELEMENT?
|
|
1133
|
-
name=ident['Element']
|
|
1134
|
-
{ this.addDef( $art, $outer, 'elements', 'element', $name.id );
|
|
1135
|
-
this.docComment( $art ); }
|
|
1136
|
-
annotationAssignment_fix[ $art ]*
|
|
1137
|
-
// TODO: we can think of making the typeSpec optional and do checks instead:
|
|
1138
|
-
// type optional with '=', type required otherwise
|
|
1139
|
-
(
|
|
1140
|
-
typeStruct[ $art ]
|
|
1141
|
-
( nullability[ $art ]
|
|
1142
|
-
requiredSemi
|
|
1143
|
-
| optionalSemi // NOT and NULL are reserved...
|
|
1144
|
-
)
|
|
1145
|
-
|
|
|
1146
|
-
':'
|
|
1147
|
-
// #ATN: referenced type name can be ASSOCIATION or ARRAY or TYPE or LOCALIZED
|
|
1148
|
-
(
|
|
1149
|
-
typeStruct[ $art ]
|
|
1150
|
-
nullability[ $art ]?
|
|
1151
|
-
requiredSemi
|
|
1152
|
-
|
|
|
1153
|
-
typeAssociationBase[ $art, true ]
|
|
1154
|
-
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1155
|
-
(
|
|
1156
|
-
typeStruct[ $art.target, true ] optionalSemi
|
|
1157
|
-
|
|
|
1158
|
-
one=ONE
|
|
1159
|
-
{ this.setMaxCardinality( $art, $one, this.numberLiteral( $one, null, '1' ) ); }
|
|
1160
|
-
typeCompoStruct[ $art.target ] optionalSemi
|
|
1161
|
-
|
|
|
1162
|
-
many=MANY
|
|
1163
|
-
{ this.setMaxCardinality( $art, $many, { literal: 'string', val: '*' } ); }
|
|
1164
|
-
typeCompoStruct[ $art.target ] optionalSemi
|
|
1165
|
-
|
|
|
1166
|
-
// we do not support `Composition of many { e }` - ambiguity ad-hoc target versus foreign keys!
|
|
1167
|
-
typeToMany[ $art ] typeAssociationElementCont[ $art ]
|
|
1168
|
-
|
|
|
1169
|
-
typeToOne[ $art ] typeAssociationElementCont[ $art ]
|
|
1170
|
-
|
|
|
1171
|
-
simplePath[ $art.target, 'artref' ] typeAssociationElementCont[ $art ]
|
|
1172
|
-
)
|
|
1173
|
-
|
|
|
1174
|
-
(
|
|
1175
|
-
array=ARRAY of=OF
|
|
1176
|
-
{ $art.items = { location: this.tokenLocation( $array, $of ) }; }
|
|
1177
|
-
| many=MANY
|
|
1178
|
-
{ $art.items = { location: this.tokenLocation( $many ) };}
|
|
1179
|
-
)
|
|
1180
|
-
// #ATN: typeRefOptArgs can start with TYPE
|
|
1181
|
-
( typeStruct[ $art.items ]
|
|
1182
|
-
nullability[ $art.items ]?
|
|
1183
|
-
| typeTypeOf[ $art.items ]
|
|
1184
|
-
nullability[ $art.items ]?
|
|
1185
|
-
{ this.docComment( $art ); }
|
|
1186
|
-
annotationAssignment_ll1[ $art ]*
|
|
1187
|
-
| typeRefOptArgs[ $art.items ]
|
|
1188
|
-
nullability[ $art.items ]? // only if not followed by `enum`
|
|
1189
|
-
{ this.docComment( $art ); }
|
|
1190
|
-
annotationAssignment_ll1[ $art ]*
|
|
1191
|
-
(
|
|
1192
|
-
{ if ($art.items.notNull) {
|
|
1193
|
-
this.message( 'syntax-unexpected-null', $art.items.notNull.location,
|
|
1194
|
-
{ keyword: $art.items.notNull.val ? 'not null' : 'null' } );
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
ENUM '{' { $art.items.enum = this.createDict(); }
|
|
1198
|
-
enumSymbolDef[ $art.items ]*
|
|
1199
|
-
'}' { this.finalizeDictOrArray( $art.items.enum ); }
|
|
1200
|
-
nullability[ $art.items ]?
|
|
1201
|
-
)?
|
|
1202
|
-
)
|
|
1203
|
-
requiredSemi // also req after struct/enum
|
|
1204
|
-
|
|
|
1205
|
-
typeTypeOf[ $art ] elementProperties[ $art ]?
|
|
1206
|
-
{ this.docComment( $art ); }
|
|
1207
|
-
annotationAssignment_ll1[ $art ]*
|
|
1208
|
-
requiredSemi // also req after foreign key spec
|
|
1209
|
-
|
|
|
1210
|
-
l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
1211
|
-
typeRefOptArgs[ $art ]
|
|
1212
|
-
{ this.docComment( $art ); }
|
|
1213
|
-
annotationAssignment_ll1[ $art ]*
|
|
1214
|
-
( elementProperties[ $art ]
|
|
1215
|
-
{ this.docComment( $art ); }
|
|
1216
|
-
annotationAssignment_ll1[ $art ]*
|
|
1217
|
-
)?
|
|
1218
|
-
requiredSemi
|
|
1219
|
-
|
|
|
1220
|
-
typeRefOptArgs[ $art ]
|
|
1221
|
-
{ this.docComment( $art ); }
|
|
1222
|
-
annotationAssignment_ll1[ $art ]*
|
|
1223
|
-
(
|
|
1224
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
1225
|
-
enumSymbolDef[ $art ]*
|
|
1226
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1227
|
-
elementProperties[ $art ]?
|
|
1228
|
-
|
|
|
1229
|
-
elementProperties[ $art ]
|
|
1230
|
-
{ this.docComment( $art ); }
|
|
1231
|
-
annotationAssignment_ll1[ $art ]*
|
|
1232
|
-
)?
|
|
1233
|
-
requiredSemi // also req after enum spec
|
|
1234
|
-
)
|
|
1235
|
-
|
|
|
1236
|
-
// this is also called for enum symbols (in EXTEND)
|
|
1237
|
-
eq='=' e=expression // never introduce AS as syntax variant of '='
|
|
1238
|
-
{
|
|
1239
|
-
if (!$allowEq || $e.expr && !$e.expr.literal )
|
|
1240
|
-
this.error( 'syntax-unsupported-calc-field', $eq );
|
|
1241
|
-
else if ($e.expr)
|
|
1242
|
-
$art.value = $e.expr;
|
|
1243
|
-
}
|
|
1244
|
-
{ this.docComment( $art ); }
|
|
1245
|
-
annotationAssignment_ll1[ $art ]* // for enum symbol def via EXTEND
|
|
1246
|
-
requiredSemi
|
|
1247
|
-
)
|
|
1248
|
-
;
|
|
1249
|
-
|
|
1250
|
-
extendElement[ art, outer ]
|
|
1251
|
-
@after{ /* #ATN 1 */ this.attachLocation( $art ); }
|
|
1252
|
-
:
|
|
1253
|
-
// #ATN: element name can be ELEMENT
|
|
1254
|
-
( expected=ELEMENT { $art.expectedKind = 'element'; } )?
|
|
1255
|
-
name=ident['Element']
|
|
1256
|
-
{ this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
|
|
1257
|
-
extendWithOptElementsOrType[ $art, $art ]
|
|
1258
|
-
;
|
|
1259
|
-
|
|
1260
|
-
selectItemDef[ outer ] locals[ art ]
|
|
1261
|
-
@after{ if ($art) this.attachLocation( $art ); }
|
|
1262
|
-
:
|
|
1263
|
-
star='*'
|
|
1264
|
-
{ $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
|
|
1265
|
-
|
|
|
1266
|
-
{ $art = {};; this.docComment( $art ); }
|
|
1267
|
-
annotationAssignment_atn[ $art ]*
|
|
1268
|
-
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
1269
|
-
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^([,.:\[@]|as)$/i ) ; } // not '{'
|
|
1270
|
-
( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
|
|
1271
|
-
( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
|
|
1272
|
-
selectItemDefBody[ $art, $outer ]
|
|
1273
|
-
;
|
|
1274
|
-
|
|
1275
|
-
selectItemDefBody[ art, outer ] locals[ assoc ]
|
|
1276
|
-
@after{ /* #ATN 2 */ }
|
|
1277
|
-
:
|
|
1278
|
-
{ $outer.push( $art ); }
|
|
1279
|
-
(
|
|
1280
|
-
e=expression { $art.value = $e.expr; }
|
|
1281
|
-
// we cannot use 'condition' instead, as long as we allow aliases without
|
|
1282
|
-
// AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
|
|
1283
|
-
( as=AS n1=ident['Item'] { $art.name = $n1.id }
|
|
1284
|
-
| n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
|
|
1285
|
-
| { this.classifyImplicitName( 'Item', $e.expr ); }
|
|
1286
|
-
)
|
|
1287
|
-
{ if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
|
|
1288
|
-
else if ($art.name) this.excludeExpected( ["'.'"] );
|
|
1289
|
-
}
|
|
1290
|
-
(
|
|
1291
|
-
{ this.reportExpandInline( $art, false ); }
|
|
1292
|
-
selectItemInlineList[ $art, 'expand' ]
|
|
1293
|
-
excludingClause[ $art ]?
|
|
1294
|
-
// TODO: we might alternatively allow AS here
|
|
1295
|
-
|
|
|
1296
|
-
{ this.reportExpandInline( $art, $as || this._input.LT(-1) ); }
|
|
1297
|
-
DOTbeforeBRACE // ...orASTERISK
|
|
1298
|
-
(
|
|
1299
|
-
selectItemInlineList[ $art, 'inline' ]
|
|
1300
|
-
excludingClause[ $art ]?
|
|
1301
|
-
|
|
|
1302
|
-
star='*'
|
|
1303
|
-
{ $art.inline = [ this.valueWithTokenLocation( '*', $star ) ]; }
|
|
1304
|
-
)
|
|
1305
|
-
)?
|
|
1306
|
-
|
|
|
1307
|
-
selectItemInlineList[ $art, 'expand' ]
|
|
1308
|
-
excludingClause[ $art ]?
|
|
1309
|
-
AS n1=ident['Item'] { $art.name = $n1.id }
|
|
1310
|
-
)
|
|
1311
|
-
{ this.docComment( $art ); }
|
|
1312
|
-
annotationAssignment_fix[ $art ]*
|
|
1313
|
-
( ':'
|
|
1314
|
-
// #ATN: typeRefOptArgs can start with TYPE, REDIRECTED, ASSOCIATION
|
|
1315
|
-
( re=REDIRECTED to=TO
|
|
1316
|
-
{ $art.target = {}; }
|
|
1317
|
-
simplePath[ $art.target, 'artref' ]
|
|
1318
|
-
(
|
|
1319
|
-
typeAssociationCont[ $art ]
|
|
1320
|
-
|
|
|
1321
|
-
{ this.docComment( $art ); }
|
|
1322
|
-
annotationAssignment_ll1[ $art ]*
|
|
1323
|
-
)
|
|
1324
|
-
| typeTypeOf[ $art ]
|
|
1325
|
-
{ this.docComment( $art ); }
|
|
1326
|
-
annotationAssignment_ll1[ $art ]*
|
|
1327
|
-
| l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
1328
|
-
typeRefOptArgs[ $art ]
|
|
1329
|
-
{ this.docComment( $art ); }
|
|
1330
|
-
annotationAssignment_ll1[ $art ]*
|
|
1331
|
-
| typeRefOptArgs[ $art ]
|
|
1332
|
-
{ this.docComment( $art ); }
|
|
1333
|
-
annotationAssignment_ll1[ $art ]*
|
|
1334
|
-
|
|
|
1335
|
-
{ $assoc = this.associationInSelectItem( $art ); }
|
|
1336
|
-
typeAssociationBase[ $assoc, false ]
|
|
1337
|
-
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1338
|
-
( typeToMany[ $assoc ] | typeToOne[ $assoc ] | simplePath[ $assoc.target, 'artref' ] )
|
|
1339
|
-
ON cond=condition
|
|
1340
|
-
{ $assoc.on=$cond.cond; }
|
|
1341
|
-
)
|
|
1342
|
-
)?
|
|
1343
|
-
;
|
|
1344
|
-
|
|
1345
|
-
bracedSelectItemListDef[ query ]
|
|
1346
|
-
:
|
|
1347
|
-
'{' { $query.columns = this.createArray(); }
|
|
1348
|
-
(
|
|
1349
|
-
selectItemDef[ $query.columns ]
|
|
1350
|
-
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1351
|
-
selectItemDef[ $query.columns ]
|
|
1352
|
-
)*
|
|
1353
|
-
)?
|
|
1354
|
-
'}' { this.finalizeDictOrArray( $query.columns ); }
|
|
1355
|
-
;
|
|
1356
|
-
|
|
1357
|
-
selectItemInlineList[ art, clause ]
|
|
1358
|
-
:
|
|
1359
|
-
'{' { $art[$clause] = this.createArray(); }
|
|
1360
|
-
(
|
|
1361
|
-
selectItemInlineDef[ $art[$clause] ]
|
|
1362
|
-
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
1363
|
-
selectItemInlineDef[ $art[$clause] ]
|
|
1364
|
-
)*
|
|
1365
|
-
)?
|
|
1366
|
-
'}' { this.finalizeDictOrArray( $art[$clause] ); }
|
|
1367
|
-
;
|
|
1368
|
-
|
|
1369
|
-
selectItemInlineDef[ outer ] locals[ art ]
|
|
1370
|
-
@after{ if ($art) this.attachLocation( $art ); }
|
|
1371
|
-
:
|
|
1372
|
-
star='*'
|
|
1373
|
-
{ $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
|
|
1374
|
-
|
|
|
1375
|
-
{ $art = {};; this.docComment( $art ); }
|
|
1376
|
-
annotationAssignment_atn[ $art ]*
|
|
1377
|
-
selectItemDefBody[ $art, $outer ]
|
|
1378
|
-
;
|
|
1379
|
-
|
|
1380
|
-
parameterListDef[ art ]
|
|
1381
|
-
:
|
|
1382
|
-
'(' { $art.params = this.createDict(); }
|
|
1383
|
-
// also empty param list (we might do some hacking later to allow reserved words)
|
|
1384
|
-
// see annotationAssignment_paren
|
|
1385
|
-
(
|
|
1386
|
-
parameterDef[ $art ]
|
|
1387
|
-
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1388
|
-
parameterDef[ $art ]
|
|
1389
|
-
)*
|
|
1390
|
-
)?
|
|
1391
|
-
')' { this.finalizeDictOrArray( $art.params ); }
|
|
1392
|
-
;
|
|
1393
|
-
|
|
1394
|
-
parameterDef[ outer ] locals[ art = {} ]
|
|
1395
|
-
@after { this.attachLocation( $art ); }
|
|
1396
|
-
:
|
|
1397
|
-
{ this.docComment( $art ); }
|
|
1398
|
-
annotationAssignment_ll1[ $art ]*
|
|
1399
|
-
name=ident['Param']
|
|
1400
|
-
{ this.addDef( $art, $outer, 'params', 'param', $name.id );
|
|
1401
|
-
this.docComment( $art ); }
|
|
1402
|
-
annotationAssignment_fix[ $art ]*
|
|
1403
|
-
typeSpec[ $art ]
|
|
1404
|
-
defaultValue[ $art ]?
|
|
1405
|
-
{ this.docComment( $art ); }
|
|
1406
|
-
annotationAssignment_ll1[ $art ]*
|
|
1407
|
-
;
|
|
1408
|
-
|
|
1409
|
-
nullability[ art ]
|
|
1410
|
-
:
|
|
1411
|
-
not=NOT n1=NULL
|
|
1412
|
-
{ $art.notNull = this.valueWithTokenLocation( true, $not, $n1 ); }
|
|
1413
|
-
|
|
|
1414
|
-
n2=NULL
|
|
1415
|
-
{ $art.notNull = this.valueWithTokenLocation( false, $n2 ); }
|
|
1416
|
-
;
|
|
1417
|
-
|
|
1418
|
-
elementProperties[ elem ]
|
|
1419
|
-
:
|
|
1420
|
-
nullability[ $elem ]
|
|
1421
|
-
defaultValue[ $elem ]?
|
|
1422
|
-
|
|
|
1423
|
-
defaultValue[ $elem ]
|
|
1424
|
-
nullability[ $elem ]?
|
|
1425
|
-
|
|
|
1426
|
-
eq='='
|
|
1427
|
-
{ this.error( 'syntax-unsupported-calc-field', $eq ); }
|
|
1428
|
-
expression
|
|
1429
|
-
;
|
|
1430
|
-
|
|
1431
|
-
// View definitions ----------------------------------------------------------
|
|
1432
|
-
|
|
1433
|
-
viewDef[ art, outer ] locals[ name = {} ]
|
|
1434
|
-
@after { this.attachLocation( $art ); }
|
|
1435
|
-
:
|
|
1436
|
-
v=VIEW simplePath[ $name, 'Entity' ]
|
|
1437
|
-
{ $art['$'+'syntax'] = 'view';
|
|
1438
|
-
this.addDef( $art, $outer, 'artifacts', 'entity', $name );
|
|
1439
|
-
this.docComment( $art ); }
|
|
1440
|
-
annotationAssignment_fix[ $art ]*
|
|
1441
|
-
(
|
|
1442
|
-
parameterListDef[ $art ]
|
|
1443
|
-
|
|
|
1444
|
-
// TODO: warning deprecated?
|
|
1445
|
-
( HideAlternatives | WITH ) { $art.params = this.createDict(); }
|
|
1446
|
-
PARAMETERS
|
|
1447
|
-
parameterDef[ $art ]
|
|
1448
|
-
( ',' parameterDef[ $art ] )* // no optional final ',' here
|
|
1449
|
-
{ this.finalizeDictOrArray( $art.params ); }
|
|
1450
|
-
)?
|
|
1451
|
-
AS qe=queryExpression { $art.query = $qe.query; }
|
|
1452
|
-
// TODO check ANTLR: bad msg with 'view V as'<eof> but 'view V as FOO' is fine
|
|
1453
|
-
requiredSemi
|
|
1454
|
-
;
|
|
1455
|
-
|
|
1456
|
-
// Type references -----------------------------------------------------------
|
|
1457
|
-
|
|
1458
|
-
includeRef[ art ] locals[ incl = {} ]
|
|
1459
|
-
:
|
|
1460
|
-
simplePath[ $incl, 'artref' ]
|
|
1461
|
-
{ if ($art.includes) $art.includes.push($incl); else $art.includes = [$incl]; }
|
|
1462
|
-
;
|
|
1463
|
-
|
|
1464
|
-
typeSpec[ art ] // for params
|
|
1465
|
-
@after{ /* #ATN 1 */ }
|
|
1466
|
-
:
|
|
1467
|
-
typeStruct[ $art ]
|
|
1468
|
-
|
|
|
1469
|
-
':'
|
|
1470
|
-
// #ATN: typeSimple can start with ARRAY or TYPE
|
|
1471
|
-
( typeStruct[ $art ]
|
|
1472
|
-
nullability[ $art ]?
|
|
1473
|
-
| typeArray[ $art ] // nullability is set in typeArray
|
|
1474
|
-
| typeTypeOf[ $art ]
|
|
1475
|
-
nullability[ $art ]?
|
|
1476
|
-
// TODO: no LOCALIZED ?
|
|
1477
|
-
| typeRefOptArgs[ $art ]
|
|
1478
|
-
nullability[ $art ]?
|
|
1479
|
-
(
|
|
1480
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
1481
|
-
enumSymbolDef[ $art ]*
|
|
1482
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
1483
|
-
)?
|
|
1484
|
-
)
|
|
1485
|
-
;
|
|
1486
|
-
|
|
1487
|
-
returnTypeSpec[ art ]
|
|
1488
|
-
@after{ /* #ATN 1 */ }
|
|
1489
|
-
:
|
|
1490
|
-
ret=RETURNS { $art.returns = { location: this.tokenLocation( $ret ), kind: 'param' }; }
|
|
1491
|
-
// #ATN: typeSimple can start with ARRAY or TYPE
|
|
1492
|
-
( typeStruct[ $art.returns ]
|
|
1493
|
-
nullability[ $art.returns ]?
|
|
1494
|
-
| typeArray[ $art.returns ] // nullability is set in typeArray
|
|
1495
|
-
| typeTypeOf[ $art.returns ]
|
|
1496
|
-
nullability[ $art.returns ]?
|
|
1497
|
-
// TODO: no LOCALIZED ?
|
|
1498
|
-
| typeRefOptArgs[ $art.returns ]
|
|
1499
|
-
nullability[ $art.returns ]?
|
|
1500
|
-
(
|
|
1501
|
-
ENUM '{' { $art.returns.enum = this.createDict(); }
|
|
1502
|
-
enumSymbolDef[ $art.returns ]*
|
|
1503
|
-
'}' { this.finalizeDictOrArray( $art.returns.enum ); }
|
|
1504
|
-
)?
|
|
1505
|
-
)
|
|
1506
|
-
|
|
1507
|
-
requiredSemi // currently for all - might change if we get rid of the misplaced annos (TODO: Now removed)
|
|
1198
|
+
requiredSemi // currently for all - might change if we get rid of the misplaced annos (TODO: Now removed)
|
|
1508
1199
|
;
|
|
1509
1200
|
|
|
1510
1201
|
|
|
@@ -1653,6 +1344,11 @@ typeArray[ art ]
|
|
|
1653
1344
|
nullability[ $art.items ]?
|
|
1654
1345
|
| typeTypeOf[ $art.items ]
|
|
1655
1346
|
nullability[ $art.items ]?
|
|
1347
|
+
(
|
|
1348
|
+
ENUM '{' { $art.items.enum = this.createDict(); }
|
|
1349
|
+
enumSymbolDef[ $art.items ]*
|
|
1350
|
+
'}' { this.finalizeDictOrArray( $art.items.enum ); }
|
|
1351
|
+
)?
|
|
1656
1352
|
| typeRefOptArgs[ $art.items ]
|
|
1657
1353
|
nullability[ $art.items ]?
|
|
1658
1354
|
(
|
|
@@ -1771,6 +1467,15 @@ cardinality[ art ] locals[ card = {} ]
|
|
|
1771
1467
|
']'
|
|
1772
1468
|
;
|
|
1773
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
|
+
|
|
1774
1479
|
foreignKey[ outer ] locals[ art = {}, elem = {} ]
|
|
1775
1480
|
@after { this.attachLocation($art); }
|
|
1776
1481
|
:
|
|
@@ -1793,210 +1498,465 @@ typeTypeOf[ art ] locals[ _sync = 'nop' ]
|
|
|
1793
1498
|
)?
|
|
1794
1499
|
;
|
|
1795
1500
|
|
|
1796
|
-
typeRefOptArgs[ art ]
|
|
1797
|
-
@init { $art.type = {}; }
|
|
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; } )?
|
|
1694
|
+
;
|
|
1695
|
+
|
|
1696
|
+
querySource[ query ]
|
|
1697
|
+
@after { this.attachLocation($query.from); }
|
|
1798
1698
|
:
|
|
1799
|
-
|
|
1699
|
+
t1=tableExpression { $query.from = $t1.table; }
|
|
1800
1700
|
(
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
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 ); } )+
|
|
1806
1706
|
)?
|
|
1807
1707
|
;
|
|
1808
1708
|
|
|
1809
|
-
|
|
1709
|
+
tableExpression returns[ table ] // TableOrJoin
|
|
1710
|
+
@after { this.attachLocation($table); }
|
|
1810
1711
|
:
|
|
1811
|
-
|
|
1712
|
+
qt=tableTerm { $table = $qt.table; }
|
|
1812
1713
|
(
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
{ $
|
|
1816
|
-
|
|
1817
|
-
(
|
|
1818
|
-
v=VARIABLE
|
|
1819
|
-
{ $art['$'+'typeArgs'].push(
|
|
1820
|
-
{ literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
|
|
1821
|
-
}
|
|
1822
|
-
|
|
|
1823
|
-
f=FLOATING
|
|
1824
|
-
{ $art['$'+'typeArgs'].push(
|
|
1825
|
-
{ literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
|
|
1826
|
-
}
|
|
1827
|
-
|
|
|
1828
|
-
tail=Number
|
|
1829
|
-
{ $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
|
|
1830
|
-
)
|
|
1831
|
-
)*
|
|
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; }
|
|
1832
1718
|
|
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1836
|
-
typeNamedArg[ $art ]
|
|
1837
|
-
)*
|
|
1838
|
-
)
|
|
1839
|
-
')'{ this.finalizeDictOrArray( $art['$'+'typeArgs']); }
|
|
1840
|
-
;
|
|
1841
|
-
|
|
1842
|
-
typeNamedArgList[ art ]
|
|
1843
|
-
:
|
|
1844
|
-
paren='('
|
|
1845
|
-
typeNamedArg[ $art ]
|
|
1846
|
-
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
1847
|
-
typeNamedArg[ $art ]
|
|
1719
|
+
crj=CROSS jn=JOIN tt=tableTerm
|
|
1720
|
+
{ if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
|
|
1848
1721
|
)*
|
|
1849
|
-
')'
|
|
1850
1722
|
;
|
|
1851
1723
|
|
|
1852
|
-
|
|
1724
|
+
tableTerm returns [ table ]
|
|
1725
|
+
@after{ /* #ATN 1 */ this.attachLocation($table); }
|
|
1853
1726
|
:
|
|
1854
|
-
|
|
1855
|
-
'
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
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
|
|
1859
1744
|
(
|
|
1860
|
-
|
|
1861
|
-
{
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
v=VARIABLE
|
|
1867
|
-
{ if ($arg && $art && $name.id) {
|
|
1868
|
-
$art[$arg] = { literal: 'string', val: 'variable', location: this.tokenLocation($v) };
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
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
|
+
)
|
|
1871
1751
|
|
|
|
1872
|
-
|
|
1873
|
-
{
|
|
1874
|
-
$art[$arg] = { literal: 'string', val: 'floating', location: this.tokenLocation($f) };
|
|
1875
|
-
}
|
|
1876
|
-
}
|
|
1752
|
+
te=tableExpression close=')'
|
|
1753
|
+
{ $table = this.surroundByParens( $te.table, $open, $close ); }
|
|
1877
1754
|
)
|
|
1878
1755
|
;
|
|
1879
1756
|
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
queryExpression returns[ query ] // QLSubqueryComplex, SubqueryComplex
|
|
1884
|
-
@after{ this.attachLocation($query); }
|
|
1757
|
+
fromPath[ qp, idkind ]
|
|
1758
|
+
@after{ this.attachLocation($qp); }
|
|
1885
1759
|
:
|
|
1886
|
-
|
|
1760
|
+
id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
1761
|
+
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
1762
|
+
| cardinalityAndFilter[ $id.id ]
|
|
1763
|
+
)?
|
|
1887
1764
|
(
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
|
1891
|
-
)
|
|
1892
|
-
qt=queryTerm
|
|
1893
|
-
{ if ($qt.query) $query = this.leftAssocBinaryOp( $query, $op, $q, $qt.query );; $ctx.q = null; }
|
|
1765
|
+
'.' id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
1766
|
+
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
1767
|
+
| cardinalityAndFilter[ $id.id ]
|
|
1768
|
+
)?
|
|
1894
1769
|
)*
|
|
1895
|
-
( ob=orderByClause[ $query ] { if ($ob.query) $query = $ob.query; } ) ?
|
|
1896
|
-
( lc=limitClause[ $query ] { if ($lc.query) $query = $lc.query; } ) ?
|
|
1897
1770
|
;
|
|
1898
1771
|
|
|
1899
|
-
|
|
1772
|
+
fromArguments[ pathStep ]
|
|
1773
|
+
@init{ if (!$pathStep) $pathStep = {}; } // grammar robustness, see test/negative/parser/NamedExpression.cds
|
|
1900
1774
|
:
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
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 ); }
|
|
1783
|
+
;
|
|
1784
|
+
|
|
1785
|
+
// Queries: columns and other clauses -------------------------------------------
|
|
1786
|
+
|
|
1787
|
+
excludingClause[ query ]
|
|
1788
|
+
:
|
|
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 ]
|
|
1795
|
+
)*
|
|
1796
|
+
'}' { this.finalizeDictOrArray( $query.excludingDict ); }
|
|
1904
1797
|
;
|
|
1905
1798
|
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
functionOrderByClause[ lhsExpr ] returns [ expr ]
|
|
1909
|
-
@after { this.attachLocation( $expr ); }
|
|
1799
|
+
projectionExclusion[ outer ] locals[ art = {} ]
|
|
1800
|
+
@after { this.attachLocation($art); }
|
|
1910
1801
|
:
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
|
|
1802
|
+
name=ident['ref']
|
|
1803
|
+
{ this.addDef( $art, $outer, 'excludingDict', '', $name.id ); }
|
|
1914
1804
|
;
|
|
1915
1805
|
|
|
1916
|
-
|
|
1917
|
-
|
|
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); }
|
|
1918
1811
|
:
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
(
|
|
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
|
|
1922
1833
|
;
|
|
1923
1834
|
|
|
1924
|
-
|
|
1925
|
-
@after
|
|
1835
|
+
selectItemDef[ outer ] locals[ art ]
|
|
1836
|
+
@after{ if ($art) this.attachLocation( $art ); }
|
|
1926
1837
|
:
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
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 ]
|
|
1930
1848
|
;
|
|
1931
1849
|
|
|
1932
|
-
|
|
1933
|
-
@after
|
|
1850
|
+
selectItemDefBody[ art, outer ] locals[ assoc ]
|
|
1851
|
+
@after{ /* #ATN 2 */ }
|
|
1934
1852
|
:
|
|
1935
|
-
|
|
1936
|
-
|
|
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
|
+
)?
|
|
1937
1918
|
;
|
|
1938
1919
|
|
|
1939
|
-
|
|
1940
|
-
@after { this.attachLocation( $wfe ); }
|
|
1920
|
+
bracedSelectItemListDef[ query ]
|
|
1941
1921
|
:
|
|
1942
|
-
{ $
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
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 ); }
|
|
1950
1930
|
;
|
|
1951
1931
|
|
|
1952
|
-
|
|
1953
|
-
@after{ /* #ATN 1 */ }
|
|
1932
|
+
selectItemInlineList[ art, clause ]
|
|
1954
1933
|
:
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
{ $wfb = { op: this.valueWithTokenLocation( 'following', $n, $f ), args: [ this.numberLiteral( $n ) ]} }
|
|
1964
|
-
|
|
|
1965
|
-
{ $wfb = {} }
|
|
1966
|
-
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] ); }
|
|
1967
1942
|
;
|
|
1968
1943
|
|
|
1969
|
-
|
|
1970
|
-
@after
|
|
1944
|
+
selectItemInlineDef[ outer ] locals[ art ]
|
|
1945
|
+
@after{ if ($art) this.attachLocation( $art ); }
|
|
1971
1946
|
:
|
|
1972
|
-
|
|
1973
|
-
{
|
|
1974
|
-
$wf.op = this.valueWithTokenLocation( 'unboundedPreceding', $u, $p );
|
|
1975
|
-
$wf.args = [];
|
|
1976
|
-
}
|
|
1977
|
-
|
|
|
1978
|
-
n=Number p=PRECEDING
|
|
1979
|
-
{
|
|
1980
|
-
$wf.op = this.valueWithTokenLocation( 'preceding', $p );
|
|
1981
|
-
$wf.args = [ this.numberLiteral( $n ) ];
|
|
1982
|
-
}
|
|
1947
|
+
star='*'
|
|
1948
|
+
{ $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
|
|
1983
1949
|
|
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
$wf.args = [];
|
|
1988
|
-
}
|
|
1950
|
+
{ $art = {};; this.docComment( $art ); }
|
|
1951
|
+
annotationAssignment_atn[ $art ]*
|
|
1952
|
+
selectItemDefBody[ $art, $outer ]
|
|
1989
1953
|
;
|
|
1990
1954
|
|
|
1991
|
-
|
|
1992
|
-
@after { this.attachLocation( $over ); }
|
|
1955
|
+
orderByClause[ inQuery ] returns [ query ]
|
|
1993
1956
|
:
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
|
|
1998
|
-
( wf=windowFrameClause { $over.args.push( $wf.wf ); } )?
|
|
1999
|
-
')'
|
|
1957
|
+
ORDER BY { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
|
|
1958
|
+
ob1=orderBySpec { $query.orderBy = [ $ob1.ob ]; }
|
|
1959
|
+
( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
|
|
2000
1960
|
;
|
|
2001
1961
|
|
|
2002
1962
|
limitClause[ inQuery ] returns [ query ]
|
|
@@ -2020,87 +1980,6 @@ orderBySpec returns[ ob ]
|
|
|
2020
1980
|
)?
|
|
2021
1981
|
;
|
|
2022
1982
|
|
|
2023
|
-
queryTerm returns[ query ]
|
|
2024
|
-
@after{ this.attachLocation($query); }
|
|
2025
|
-
:
|
|
2026
|
-
qt1=queryPrimary { $query = $qt1.query; }
|
|
2027
|
-
(
|
|
2028
|
-
intersect=INTERSECT quantifier=DISTINCT?
|
|
2029
|
-
qt=queryPrimary
|
|
2030
|
-
{ $query = this.leftAssocBinaryOp( $query, $intersect, $quantifier, $qt.query );
|
|
2031
|
-
$ctx.quantifier = null; } // reset for loop
|
|
2032
|
-
)*
|
|
2033
|
-
;
|
|
2034
|
-
|
|
2035
|
-
queryPrimary returns[ query = {} ]
|
|
2036
|
-
@after { this.attachLocation($query); }
|
|
2037
|
-
:
|
|
2038
|
-
open='(' qe=queryExpression close=')'
|
|
2039
|
-
{ $query = this.surroundByParens( $qe.query, $open, $close ); }
|
|
2040
|
-
|
|
|
2041
|
-
select=SELECT
|
|
2042
|
-
{ $query = { op: this.valueWithTokenLocation( 'SELECT', $select ), location: this.startLocation() }; }
|
|
2043
|
-
(
|
|
2044
|
-
FROM querySource[ $query ]
|
|
2045
|
-
(
|
|
2046
|
-
mixin=MIXIN '{' { $query.mixin = this.createDict(); }
|
|
2047
|
-
mixinElementDef[ $query ]*
|
|
2048
|
-
'}' { this.finalizeDictOrArray( $query.mixin ); }
|
|
2049
|
-
INTO
|
|
2050
|
-
)?
|
|
2051
|
-
( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
|
|
2052
|
-
{ $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
|
|
2053
|
-
)?
|
|
2054
|
-
bracedSelectItemListDef[ $query, 'columns' ]?
|
|
2055
|
-
excludingClause[ $query ]?
|
|
2056
|
-
|
|
|
2057
|
-
( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
|
|
2058
|
-
{ $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
|
|
2059
|
-
)?
|
|
2060
|
-
{ $query.columns = []; } // set it early to avoid "wildcard" errors
|
|
2061
|
-
selectItemDef[ $query.columns ]
|
|
2062
|
-
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
2063
|
-
selectItemDef[ $query.columns ]
|
|
2064
|
-
)*
|
|
2065
|
-
FROM querySource[ $query ]
|
|
2066
|
-
)
|
|
2067
|
-
( WHERE cond=condition { $query.where = $cond.cond; } )?
|
|
2068
|
-
(
|
|
2069
|
-
GROUP BY
|
|
2070
|
-
e1=expression { $query.groupBy = [ $e1.expr ]; }
|
|
2071
|
-
( ',' en=expression { $query.groupBy.push( $en.expr ); } )*
|
|
2072
|
-
)?
|
|
2073
|
-
( HAVING having=condition { $query.having = $having.cond; } )?
|
|
2074
|
-
;
|
|
2075
|
-
|
|
2076
|
-
querySource[ query ]
|
|
2077
|
-
@after { this.attachLocation($query.from); }
|
|
2078
|
-
:
|
|
2079
|
-
t1=tableExpression { $query.from = $t1.table; }
|
|
2080
|
-
(
|
|
2081
|
-
{ const location = this.tokenLocation( this.getCurrentToken() );
|
|
2082
|
-
$query.from = { op: { val: 'join', location },
|
|
2083
|
-
join: { val: 'cross', location },
|
|
2084
|
-
args: [$t1.table] }; }
|
|
2085
|
-
( ',' tn=tableExpression { if ($tn.table) $query.from.args.push( $tn.table ); } )+
|
|
2086
|
-
)?
|
|
2087
|
-
;
|
|
2088
|
-
|
|
2089
|
-
tableExpression returns[ table ] // TableOrJoin
|
|
2090
|
-
@after { this.attachLocation($table); }
|
|
2091
|
-
:
|
|
2092
|
-
qt=tableTerm { $table = $qt.table; }
|
|
2093
|
-
(
|
|
2094
|
-
join=joinOp[ $table ] { $table = $join.table; }
|
|
2095
|
-
te=tableExpression
|
|
2096
|
-
{ if (!$table) { $table = {}; } else if ($te.table) $table.args.push( $te.table ); }
|
|
2097
|
-
ON cond=condition { $table.on = $cond.cond; }
|
|
2098
|
-
|
|
|
2099
|
-
crj=CROSS jn=JOIN tt=tableTerm
|
|
2100
|
-
{ if (!$table) { $table = {}; } $table = this.leftAssocBinaryOp( $table, $jn, $crj, $tt.table, 'join' ); }
|
|
2101
|
-
)*
|
|
2102
|
-
;
|
|
2103
|
-
|
|
2104
1983
|
joinOp[ left ] returns[ table ] locals [ join ]
|
|
2105
1984
|
:
|
|
2106
1985
|
( op=JOIN { $join = 'inner'; }
|
|
@@ -2148,55 +2027,7 @@ joinCardinality returns [ joinCard ]
|
|
|
2148
2027
|
)
|
|
2149
2028
|
;
|
|
2150
2029
|
|
|
2151
|
-
|
|
2152
|
-
@after{ /* #ATN 1 */ this.attachLocation($table); }
|
|
2153
|
-
:
|
|
2154
|
-
{ $table = { path: [], scope: 0 }; }
|
|
2155
|
-
fromPath[ $table, 'artref']
|
|
2156
|
-
( ':'
|
|
2157
|
-
{ $table.scope = $table.path.length; }
|
|
2158
|
-
fromPath[ $table, 'ref']
|
|
2159
|
-
)?
|
|
2160
|
-
( AS n1=ident['FromAlias'] { $table.name = $n1.id }
|
|
2161
|
-
| n2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $n2.id ); }
|
|
2162
|
-
// if we would use rule `ident`, we would either had to make all JOIN
|
|
2163
|
-
// kinds reserved or introduce ATN
|
|
2164
|
-
)?
|
|
2165
|
-
// ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
|
|
2166
|
-
{ if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
|
|
2167
|
-
|
|
|
2168
|
-
open='('
|
|
2169
|
-
// #ATN: The following alternative is not LL1, because both can start with
|
|
2170
|
-
// left-paren, but queryExpression has SELECT after initial left-parens
|
|
2171
|
-
(
|
|
2172
|
-
qe=queryExpression close=')'
|
|
2173
|
-
{ $table = this.surroundByParens( $qe.query, $open, $close, true ); }
|
|
2174
|
-
( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table aliass
|
|
2175
|
-
| a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
|
|
2176
|
-
// not using ident` to have a similar behavior to above
|
|
2177
|
-
)
|
|
2178
|
-
|
|
|
2179
|
-
te=tableExpression close=')'
|
|
2180
|
-
{ $table = this.surroundByParens( $te.table, $open, $close ); }
|
|
2181
|
-
)
|
|
2182
|
-
;
|
|
2183
|
-
|
|
2184
|
-
fromPath[ qp, idkind ]
|
|
2185
|
-
@after{ this.attachLocation($qp); }
|
|
2186
|
-
:
|
|
2187
|
-
id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
2188
|
-
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
2189
|
-
| cardinalityAndFilter[ $id.id ]
|
|
2190
|
-
)?
|
|
2191
|
-
(
|
|
2192
|
-
'.' id=ident[$idkind] { this.pushIdent( $qp.path, $id.id ); }
|
|
2193
|
-
( fromArguments[ $id.id ] cardinalityAndFilter[ $id.id ]?
|
|
2194
|
-
| cardinalityAndFilter[ $id.id ]
|
|
2195
|
-
)?
|
|
2196
|
-
)*
|
|
2197
|
-
;
|
|
2198
|
-
|
|
2199
|
-
// Conditions and expressions ------------------------------------------------
|
|
2030
|
+
// Conditions and expressions ---------------------------------------------------
|
|
2200
2031
|
|
|
2201
2032
|
// With "separate" `condition` and `expression` rules, we have long LL
|
|
2202
2033
|
// ambiguities (not so with LALR used in Bison) with initial left parentheses:
|
|
@@ -2212,221 +2043,214 @@ fromPath[ qp, idkind ]
|
|
|
2212
2043
|
// be then ensured by code (either in actions of the grammar or in a check
|
|
2213
2044
|
// phase - to be discussed).
|
|
2214
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
|
+
//
|
|
2215
2050
|
// ANTLR4s left-recursion feature cannot be used as we will have rule
|
|
2216
2051
|
// arguments.
|
|
2217
2052
|
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
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 ); }
|
|
2224
2060
|
:
|
|
2225
2061
|
c1=conditionAnd { $args.push($c1.cond); }
|
|
2226
|
-
(
|
|
2062
|
+
(
|
|
2063
|
+
or=OR { this.pushXprToken( $args ); }
|
|
2064
|
+
c2=conditionAnd { $args.push($c2.cond); }
|
|
2065
|
+
)*
|
|
2227
2066
|
;
|
|
2228
2067
|
|
|
2229
|
-
conditionAnd returns [ cond ] locals
|
|
2230
|
-
@after{
|
|
2231
|
-
$cond = ($args.length === 1)
|
|
2232
|
-
? $args[0]
|
|
2233
|
-
: this.attachLocation({ op: $andl[0], args: $args });
|
|
2234
|
-
}
|
|
2068
|
+
conditionAnd returns [ cond ] locals[ args = [] ]
|
|
2069
|
+
@after{ $cond = this.argsExpression( $args, null, 'and' ); } // 'and' used by A2J and checks
|
|
2235
2070
|
:
|
|
2236
2071
|
c1=conditionTerm { $args.push($c1.cond); }
|
|
2237
|
-
(
|
|
2072
|
+
(
|
|
2073
|
+
and=AND { this.pushXprToken( $args ); }
|
|
2074
|
+
c2=conditionTerm { $args.push($c2.cond); }
|
|
2075
|
+
)*
|
|
2238
2076
|
;
|
|
2239
2077
|
|
|
2240
2078
|
// Note: New operators need to be added to functionExpressionOperatorsRequireParentheses[] in toCdl.js.
|
|
2241
|
-
conditionTerm returns [ cond ]
|
|
2242
|
-
@after{
|
|
2243
|
-
if ($cond) { this.attachLocation($cond); } else { $cond = $expr.expr; }
|
|
2244
|
-
}
|
|
2079
|
+
conditionTerm returns [ cond ] locals[ args = [] ]
|
|
2080
|
+
@after{ $cond = this.argsExpression( $args, null, '=' ); }// op: '=' used by A2J and checks
|
|
2245
2081
|
:
|
|
2246
|
-
nt=NOT
|
|
2247
|
-
|
|
2082
|
+
nt=NOT { this.pushXprToken( $args ); }
|
|
2083
|
+
ct=conditionTerm { $args.push( $ct.cond ); }
|
|
2248
2084
|
|
|
|
2249
|
-
|
|
2085
|
+
EXISTS { this.pushXprToken( $args ); }
|
|
2250
2086
|
(
|
|
2251
2087
|
open='(' qe=queryExpression close=')'
|
|
2252
|
-
{ $
|
|
2253
|
-
args: [ this.surroundByParens( $qe.query, $open, $close, true ) ] }; }
|
|
2088
|
+
{ $args.push( this.surroundByParens( $qe.query, $open, $close, true ) ); }
|
|
2254
2089
|
|
|
|
2255
2090
|
qm=( HideAlternatives | '?' )
|
|
2256
|
-
{ $
|
|
2257
|
-
{ param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' }
|
|
2258
|
-
] };
|
|
2091
|
+
{ $args.push( { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' } );
|
|
2259
2092
|
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
|
|
2260
2093
|
}
|
|
2261
2094
|
|
|
|
2262
|
-
ep=valuePath[ 'ref' ]
|
|
2263
|
-
{ $ep.qp['$'+'expected'] = 'exists';
|
|
2264
|
-
$cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [ $ep.qp ] };
|
|
2265
|
-
}
|
|
2095
|
+
ep=valuePath[ 'ref' ] { $args.push( $ep.qp ); }
|
|
2096
|
+
{ $ep.qp['$'+'expected'] = 'exists'; } // hm, really in parser? what about CSN input?
|
|
2266
2097
|
)
|
|
2267
2098
|
|
|
|
2268
|
-
expr=expression
|
|
2099
|
+
expr=expression { $args.push( $expr.expr ); }
|
|
2269
2100
|
(
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
{ $cond.quantifier = this.valueWithTokenLocation( $asa.text.toLowerCase(), $asa ); }
|
|
2274
|
-
)?
|
|
2275
|
-
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 ); }
|
|
2276
2104
|
|
|
|
2277
|
-
IS (
|
|
2278
|
-
|
|
2105
|
+
IS { this.pushXprToken( $args ); }
|
|
2106
|
+
( NOT { this.pushXprToken( $args ); } )?
|
|
2107
|
+
NULL { this.pushXprToken( $args ); }
|
|
2279
2108
|
|
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
{ if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
|
|
2109
|
+
NOT { this.pushXprToken( $args ); }
|
|
2110
|
+
predicate[ $args ]
|
|
2283
2111
|
|
|
|
2284
|
-
|
|
2285
|
-
predicate[ $cond, false ]
|
|
2286
|
-
{ if (!$cond.op) $cond = null; } // predicate failed to parse, avoid subseqential errors
|
|
2112
|
+
predicate[ $args ]
|
|
2287
2113
|
)? // optional: for conditions in parentheses
|
|
2288
2114
|
;
|
|
2289
2115
|
|
|
2290
2116
|
// Note: New operators need to be added to functionExpressionOperatorsRequireParentheses[] in toCdl.js.
|
|
2291
|
-
predicate[
|
|
2292
|
-
// As an alternative, we could have a `negated` properties for the operations
|
|
2293
|
-
// `isNull`(!), `in`, `between` and `like` (or produce the same AST as for
|
|
2294
|
-
// NOT (a BETWEEN b AND c)
|
|
2117
|
+
predicate[ args ]
|
|
2295
2118
|
:
|
|
2296
|
-
|
|
2297
|
-
|
|
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
|
|
2298
2122
|
|
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
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 ); }
|
|
2302
2127
|
|
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
( 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
|
+
)?
|
|
2306
2133
|
;
|
|
2307
2134
|
|
|
2308
|
-
expression returns [ expr ]
|
|
2309
|
-
@after{
|
|
2135
|
+
expression returns [ expr ] locals [ args = [] ]
|
|
2136
|
+
@after{ $expr = this.argsExpression( $args ); }
|
|
2310
2137
|
:
|
|
2311
|
-
e1=expressionSum { $
|
|
2138
|
+
e1=expressionSum { $args.push( $e1.expr ); }
|
|
2312
2139
|
(
|
|
2313
|
-
|
|
2314
|
-
{
|
|
2315
|
-
$expr = {
|
|
2316
|
-
op: this.valueWithTokenLocation( '||', $or ), args: [$expr, $e2.expr],
|
|
2317
|
-
location: this.combinedLocation( $expr, $e2.expr ) };
|
|
2318
|
-
}
|
|
2140
|
+
'||' { this.pushXprToken( $args ); }
|
|
2141
|
+
e2=expressionSum { $args.push( $e2.expr ); }
|
|
2319
2142
|
)*
|
|
2320
2143
|
;
|
|
2321
2144
|
|
|
2322
|
-
expressionSum returns [ expr ]
|
|
2323
|
-
@after{
|
|
2145
|
+
expressionSum returns [ expr ] locals [ args = [] ]
|
|
2146
|
+
@after{ $expr = this.argsExpression( $args ); }
|
|
2324
2147
|
:
|
|
2325
|
-
e1=expressionFactor { $
|
|
2148
|
+
e1=expressionFactor { $args.push( $e1.expr ); }
|
|
2326
2149
|
(
|
|
2327
|
-
|
|
2328
|
-
{
|
|
2329
|
-
$expr = {
|
|
2330
|
-
op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
|
|
2331
|
-
location: this.combinedLocation( $expr, $e2.expr ) };
|
|
2332
|
-
}
|
|
2150
|
+
( '+' | '-' ) { this.pushXprToken( $args ); }
|
|
2151
|
+
e2=expressionFactor { $args.push( $e2.expr ); }
|
|
2333
2152
|
)*
|
|
2334
2153
|
;
|
|
2335
2154
|
|
|
2336
|
-
expressionFactor returns [ expr ]
|
|
2337
|
-
@after{
|
|
2155
|
+
expressionFactor returns [ expr ] locals [ args = [] ]
|
|
2156
|
+
@after{ $expr = this.argsExpression( $args ); }
|
|
2338
2157
|
:
|
|
2339
|
-
e1=expressionTerm { $
|
|
2158
|
+
e1=expressionTerm { $args.push( $e1.expr ); }
|
|
2340
2159
|
(
|
|
2341
|
-
|
|
2342
|
-
{
|
|
2343
|
-
$expr = {
|
|
2344
|
-
op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
|
|
2345
|
-
location: this.combinedLocation( $expr, $e2.expr ) };
|
|
2346
|
-
}
|
|
2160
|
+
( '*' | '/' ) { this.pushXprToken( $args ); }
|
|
2161
|
+
e2=expressionTerm { $args.push( $e2.expr ); }
|
|
2347
2162
|
)*
|
|
2348
2163
|
;
|
|
2349
2164
|
|
|
2350
|
-
expressionTerm returns [ expr ] locals [
|
|
2351
|
-
@after{ /* #ATN 1 */ this.
|
|
2165
|
+
expressionTerm returns [ expr ] locals [ args = [] ]
|
|
2166
|
+
@after{ /* #ATN 1 */ $expr = this.argsExpression( $args ); }
|
|
2352
2167
|
:
|
|
2353
|
-
|
|
2354
|
-
|
|
2168
|
+
( '+' | '-' ) { this.pushXprToken( $args ); }
|
|
2169
|
+
e1=expressionTerm // prefix op or part of the number
|
|
2170
|
+
{ this.signedExpression( $args, $e1.expr ); }
|
|
2171
|
+
|
|
|
2172
|
+
val=literalValue { $args.push( $val.val ); }
|
|
2355
2173
|
|
|
|
2174
|
+
sf=specialFunction { $args.push( $sf.ret ); }
|
|
2175
|
+
|
|
|
2176
|
+
CASE { this.pushXprToken( $args ); }
|
|
2356
2177
|
(
|
|
2357
|
-
|
|
2358
|
-
{ $
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
ca=CASE
|
|
2364
|
-
{ $expr = { op : this.valueWithTokenLocation( 'case', $ca ), args: [] }; }
|
|
2365
|
-
(
|
|
2366
|
-
e2=expression { $expr.args.push($e2.expr); }
|
|
2367
|
-
( ow=WHEN ew=expression THEN e3=expression
|
|
2368
|
-
{ $expr.args.push( this.createPrefixOp( $ow, [ $ew.expr, $e3.expr ] ) ); }
|
|
2369
|
-
)+
|
|
2370
|
-
|
|
|
2371
|
-
( ow=WHEN c=condition THEN e3=expression
|
|
2372
|
-
{ $expr.args.push( this.createPrefixOp( $ow, [ $c.cond, $e3.expr ] ) ); }
|
|
2373
|
-
)+
|
|
2374
|
-
)
|
|
2375
|
-
( el=ELSE e4=expression
|
|
2376
|
-
{ $expr.args.push( this.createPrefixOp( $el, [ $e4.expr ] ) ); }
|
|
2377
|
-
)?
|
|
2378
|
-
END
|
|
2379
|
-
|
|
|
2380
|
-
ne=NEW nqp=valuePath[ 'ref', null] // token rewrite for NEW
|
|
2381
|
-
// please note: there will be no compiler-supported code completion after NEW
|
|
2382
|
-
{ $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
|
|
2383
|
-
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
|
+
)+
|
|
2384
2184
|
|
|
|
2385
|
-
vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
|
|
2386
|
-
{ this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
|
|
2387
2185
|
(
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
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 ); }}
|
|
2399
2217
|
|
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
{ $expr = { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' };
|
|
2404
|
-
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
|
|
2218
|
+
pp=Number
|
|
2219
|
+
{ $args.push( { param: this.numberLiteral( $pp ), scope: 'param' } );
|
|
2220
|
+
this.csnParseOnly( 'syntax-unsupported-param', [ $pp ], { '#': 'positional', code: ':' + $pp.text } );
|
|
2405
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 ) ); }
|
|
2406
2237
|
|
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
{
|
|
2420
|
-
if ($expr.length > 1)
|
|
2421
|
-
$expr = { op: this.valueWithTokenLocation( ',', $open ), args: $expr };
|
|
2422
|
-
else if ($expr[0]) // can be `null` if condition failed to parse
|
|
2423
|
-
$expr = this.surroundByParens( $expr[0], $open, $close );
|
|
2424
|
-
}
|
|
2425
|
-
)
|
|
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
|
+
}
|
|
2426
2250
|
)
|
|
2427
2251
|
;
|
|
2428
2252
|
|
|
2429
|
-
specialFunction returns [ ret = {
|
|
2253
|
+
specialFunction returns [ ret = {} ] locals[ art = {} ]
|
|
2430
2254
|
:
|
|
2431
2255
|
ca=CAST '(' // see createArray() in action
|
|
2432
2256
|
{
|
|
@@ -2440,13 +2264,25 @@ specialFunction returns [ ret = { } ] locals[ art = {} ]
|
|
|
2440
2264
|
{
|
|
2441
2265
|
$ret.args.push( $e.expr );
|
|
2442
2266
|
}
|
|
2443
|
-
')' { this.finalizeDictOrArray( $ret.args ); }
|
|
2267
|
+
')' { this.finalizeDictOrArray( $ret.args ); }
|
|
2268
|
+
;
|
|
2269
|
+
|
|
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
|
+
)*
|
|
2444
2284
|
;
|
|
2445
2285
|
|
|
2446
|
-
// query path includes aggregation:
|
|
2447
|
-
// ( COUNT | MIN | MAX | SUM | AVG | STDDEV | VAR )
|
|
2448
|
-
// '(' ( '*' | expression | ALL expression | DISTINCT expression_list ) ')'
|
|
2449
|
-
|
|
2450
2286
|
valuePath[ category, location = null ] returns[ qp = { path: [] } ] locals[ _sync = 'nop' ]
|
|
2451
2287
|
@init { $qp.location = location || this.startLocation(); }
|
|
2452
2288
|
@after{ this.attachLocation($qp); }
|
|
@@ -2465,19 +2301,6 @@ valuePath[ category, location = null ] returns[ qp = { path: [] } ] locals[ _syn
|
|
|
2465
2301
|
)*
|
|
2466
2302
|
;
|
|
2467
2303
|
|
|
2468
|
-
fromArguments[ pathStep ]
|
|
2469
|
-
@init{ if (!$pathStep) $pathStep = {}; } // grammar robustness, see test/negative/parser/NamedExpression.cds
|
|
2470
|
-
:
|
|
2471
|
-
'(' { $pathStep.args = this.createDict(); $pathStep['$'+'syntax'] = ':'; } // necessary?
|
|
2472
|
-
name=ident['paramname'] ':'
|
|
2473
|
-
namedExpression[ $pathStep, $name.id ]
|
|
2474
|
-
( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
|
|
2475
|
-
name=ident['paramname'] ':'
|
|
2476
|
-
namedExpression[ $pathStep, $name.id ]
|
|
2477
|
-
)*
|
|
2478
|
-
')' { this.finalizeDictOrArray( $pathStep.args ); }
|
|
2479
|
-
;
|
|
2480
|
-
|
|
2481
2304
|
pathArguments[ pathStep, considerSpecial ]
|
|
2482
2305
|
@init{
|
|
2483
2306
|
if (!$pathStep) $pathStep = {}; // grammar robustness, see test/negative/parser/NamedExpression.cds
|
|
@@ -2517,11 +2340,9 @@ pathArguments[ pathStep, considerSpecial ]
|
|
|
2517
2340
|
funcExpression[ $pathStep, $considerSpecial ]
|
|
2518
2341
|
)*
|
|
2519
2342
|
// Note: We can't move this into funcExpression, or we would increase the ATN count because of `,` amiguity.
|
|
2520
|
-
( ob=
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
$pathStep.args[$pathStep.args.length - 1] = $ob.expr;
|
|
2524
|
-
}
|
|
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; }
|
|
2525
2346
|
)?
|
|
2526
2347
|
|
|
|
2527
2348
|
{ $pathStep.args = this.createArray(); }
|
|
@@ -2543,32 +2364,29 @@ namedExpression[ pathStep, id ]
|
|
|
2543
2364
|
}
|
|
2544
2365
|
;
|
|
2545
2366
|
|
|
2546
|
-
funcExpression[ pathStep, considerSpecial ] locals[ args ]
|
|
2367
|
+
funcExpression[ pathStep, considerSpecial ] locals[ args = [] ]
|
|
2547
2368
|
@init { this.prepareGenericKeywords( $considerSpecial ); }
|
|
2369
|
+
@after{ $pathStep.args.push( this.argsExpression( $args, true ) ); }
|
|
2548
2370
|
:
|
|
2549
2371
|
(
|
|
2550
2372
|
expr=expression
|
|
2551
|
-
{ $
|
|
2373
|
+
{ $args.push( $expr.expr ); }
|
|
2552
2374
|
|
|
|
2553
2375
|
GenericExpr // keyword as replacement for expression, like '*'
|
|
2554
|
-
{
|
|
2376
|
+
{ this.pushXprToken( $args ); }
|
|
2555
2377
|
|
|
|
2556
2378
|
GenericIntro // keyword as introduction of expression, like DISTINCT
|
|
2557
|
-
{
|
|
2558
|
-
expr=expression
|
|
2559
|
-
{ $args = this.setLastAsXpr( $pathStep.args );
|
|
2560
|
-
$args.push( $expr.expr ); }
|
|
2379
|
+
{ this.pushXprToken( $args ); }
|
|
2380
|
+
expr=expression { $args.push( $expr.expr ); }
|
|
2561
2381
|
|
|
|
2562
2382
|
// Rule 'pathArguments' makes a decision based on the first two lookahead
|
|
2563
2383
|
// tokens of this rule → we need to list tokens which would be changed to
|
|
2564
2384
|
// GenericExpr or GenericIntro, and are not already covered by 'expression'
|
|
2565
2385
|
{ this.reportErrorForGenericKeyword(); }
|
|
2566
|
-
( HideAlternatives | '*' | ALL | DISTINCT )
|
|
2386
|
+
( HideAlternatives | '*' | ALL | DISTINCT ) { this.pushXprToken( $args ); }
|
|
2567
2387
|
// now continue parsing like GenericExpr:
|
|
2568
|
-
{ $pathStep.args.push( this.xprToken() ); }
|
|
2569
2388
|
)
|
|
2570
2389
|
(
|
|
2571
|
-
{ if (!$args) $args = this.setLastAsXpr( $pathStep.args ); }
|
|
2572
2390
|
(
|
|
2573
2391
|
{ this.prepareGenericKeywords( $considerSpecial, 'separator' ); }
|
|
2574
2392
|
(
|
|
@@ -2582,26 +2400,108 @@ funcExpression[ pathStep, considerSpecial ] locals[ args ]
|
|
|
2582
2400
|
{ this.reportErrorForGenericKeyword(); }
|
|
2583
2401
|
( HideAlternatives | Identifier | FROM | IN | WITH | GROUP )
|
|
2584
2402
|
)
|
|
2585
|
-
{
|
|
2586
|
-
this.prepareGenericKeywords( $considerSpecial, 'expr' );
|
|
2587
|
-
}
|
|
2403
|
+
{ this.pushXprToken( $args );
|
|
2404
|
+
this.prepareGenericKeywords( $considerSpecial, 'expr' ); }
|
|
2588
2405
|
(
|
|
2589
|
-
expr=expression
|
|
2590
|
-
{ $args.push( $expr.expr ); }
|
|
2406
|
+
expr=expression { $args.push( $expr.expr ); }
|
|
2591
2407
|
|
|
|
2592
|
-
GenericExpr
|
|
2593
|
-
{ $args.push( this.xprToken() ); }
|
|
2408
|
+
GenericExpr { this.pushXprToken( $args ); }
|
|
2594
2409
|
|
|
|
2595
2410
|
{ this.reportErrorForGenericKeyword(); }
|
|
2596
2411
|
// Again, we need to list tokens which could make it to GenericExpr
|
|
2597
2412
|
// and which do not start an expression
|
|
2598
|
-
( HideAlternatives | ALL )
|
|
2599
|
-
{ $args.push( this.xprToken() ); }
|
|
2413
|
+
( HideAlternatives | ALL ) { this.pushXprToken( $args ); }
|
|
2600
2414
|
)
|
|
2601
2415
|
)+
|
|
2602
2416
|
)?
|
|
2603
2417
|
;
|
|
2604
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
|
+
|
|
2605
2505
|
cardinalityAndFilter[ pathStep ] locals [ _sync = 'nop' ]
|
|
2606
2506
|
:
|
|
2607
2507
|
'['
|
|
@@ -2633,13 +2533,171 @@ optionalWhereForFilter
|
|
|
2633
2533
|
// one of the expected ones.
|
|
2634
2534
|
{
|
|
2635
2535
|
var text = this.getCurrentToken().text.toUpperCase();
|
|
2636
|
-
if (!['WHERE','GROUP','ORDER','LIMIT'].includes( text )) return;
|
|
2536
|
+
if (!['WHERE','GROUP','ORDER','LIMIT'].includes( text )) return $ctx;
|
|
2637
2537
|
// TODO: should we somehow add those keywords to $(EXPECTED)?
|
|
2638
2538
|
}
|
|
2639
2539
|
WHERE
|
|
2640
2540
|
;
|
|
2641
2541
|
|
|
2642
|
-
//
|
|
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
|
+
;
|
|
2643
2701
|
|
|
2644
2702
|
annoValue[ assignment ]
|
|
2645
2703
|
:
|
|
@@ -2768,6 +2826,8 @@ annoSubValue returns[ val = {} ]
|
|
|
2768
2826
|
( annotationPathVariant[ $val ] )?
|
|
2769
2827
|
;
|
|
2770
2828
|
|
|
2829
|
+
// Literal values and identifiers -----------------------------------------------
|
|
2830
|
+
|
|
2771
2831
|
literalValue returns[ val ] locals[ tok ]
|
|
2772
2832
|
@init{ $tok = this.getCurrentToken(); }
|
|
2773
2833
|
@after { this.attachLocation($val); }
|
|
@@ -2791,47 +2851,7 @@ literalValue returns[ val ] locals[ tok ]
|
|
|
2791
2851
|
{ $val = this.quotedLiteral( $tok ); }
|
|
2792
2852
|
;
|
|
2793
2853
|
|
|
2794
|
-
|
|
2795
|
-
@after { this.attachLocation($art); }
|
|
2796
|
-
// Due to error recovery, rule `ident` can return with value `null`. Set the
|
|
2797
|
-
// path as broken in this case.
|
|
2798
|
-
:
|
|
2799
|
-
head=ident[ $category ]
|
|
2800
|
-
{ if (!$art.path) $art.path = []; this.pushIdent( $art.path, $head.id );
|
|
2801
|
-
if ($category === 'artref') $art.scope = 0;
|
|
2802
|
-
}
|
|
2803
|
-
(
|
|
2804
|
-
'.' tail=ident[ $category ] { this.pushIdent( $art.path, $tail.id ); }
|
|
2805
|
-
)*
|
|
2806
|
-
;
|
|
2807
|
-
|
|
2808
|
-
annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
|
|
2809
|
-
@after { this.attachLocation($art); }
|
|
2810
|
-
// Due to error recovery, rule `ident` can return with value `null`. Set the
|
|
2811
|
-
// path as broken in this case.
|
|
2812
|
-
:
|
|
2813
|
-
head=ident[ $category ]
|
|
2814
|
-
{ $art.path = []; this.pushIdent( $art.path, $head.id, $headat );
|
|
2815
|
-
if ($category === 'artref') $art.scope = 0;
|
|
2816
|
-
}
|
|
2817
|
-
(
|
|
2818
|
-
'.' at='@'? tail=ident[ $category ]
|
|
2819
|
-
{ this.pushIdent( $art.path, $tail.id, $at );
|
|
2820
|
-
// Otherwise, $at may continue to be set after one `.@anno` segment.
|
|
2821
|
-
$ctx.at = null;
|
|
2822
|
-
}
|
|
2823
|
-
)*
|
|
2824
|
-
;
|
|
2825
|
-
|
|
2826
|
-
annotationPathVariant[ art ] locals[ variant = {} ]
|
|
2827
|
-
@after { this.attachLocation($art); }
|
|
2828
|
-
:
|
|
2829
|
-
// TODO: warning for space after '#'
|
|
2830
|
-
'#' { this.meltKeywordToIdentifier(); }
|
|
2831
|
-
simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
|
|
2832
|
-
;
|
|
2833
|
-
|
|
2834
|
-
// Identifier and non-reserved keywords --------------------------------------
|
|
2854
|
+
// #IDENT - keep this comment here, used in scripts/linter/lintGrammar.js
|
|
2835
2855
|
|
|
2836
2856
|
identNoKeyword[ category ] returns[ id ] // for aliases without AS
|
|
2837
2857
|
@after{ $id = this.identAst( $stop, $category ); }
|
|
@@ -2866,7 +2886,6 @@ ident[ category ] returns[ id ]
|
|
|
2866
2886
|
| DEFINE
|
|
2867
2887
|
| DEFINITIONS
|
|
2868
2888
|
| DESC
|
|
2869
|
-
| ELEMENT
|
|
2870
2889
|
| ELEMENTS
|
|
2871
2890
|
| ELSE
|
|
2872
2891
|
| END
|
|
@@ -2896,7 +2915,6 @@ ident[ category ] returns[ id ]
|
|
|
2896
2915
|
| LIMIT
|
|
2897
2916
|
| LOCALIZED
|
|
2898
2917
|
| MANY
|
|
2899
|
-
| MASKED
|
|
2900
2918
|
| MINUS
|
|
2901
2919
|
| MIXIN
|
|
2902
2920
|
| NAMESPACE
|
|
@@ -2927,7 +2945,7 @@ ident[ category ] returns[ id ]
|
|
|
2927
2945
|
| VIEW
|
|
2928
2946
|
;
|
|
2929
2947
|
|
|
2930
|
-
|
|
2948
|
+
// LEXER ------------------------------------------------------------------------
|
|
2931
2949
|
|
|
2932
2950
|
WhiteSpace // like \s in JavaScript RegExp
|
|
2933
2951
|
: // LineTerminator | [\t\f\v\u00A0\uFEFF] | Zs
|
|
@@ -2940,10 +2958,10 @@ Comment : '/*' .*? '*/' -> channel(HIDDEN);
|
|
|
2940
2958
|
|
|
2941
2959
|
LineComment : '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);
|
|
2942
2960
|
|
|
2943
|
-
//
|
|
2961
|
+
// Literal values ---------------------------------------------------------------
|
|
2944
2962
|
|
|
2945
2963
|
// for syntactic code-completion: Combine all three string styles
|
|
2946
|
-
// Note: Use rule `string` instead as that also parses escape sequences!
|
|
2964
|
+
// Note: Use rule `string` instead as that also parses escape sequences! (TODO: ???)
|
|
2947
2965
|
String : SingleLineString
|
|
2948
2966
|
| MultiLineString
|
|
2949
2967
|
| MutlLineStringBlock;
|
|
@@ -3063,7 +3081,7 @@ DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
|
|
|
3063
3081
|
DEFINE : [dD][eE][fF][iI][nN][eE] ;
|
|
3064
3082
|
DEFINITIONS : [dD][eE][fF][iI][nN][iI][tT][iI][oO][nN][sS] ;
|
|
3065
3083
|
DESC : [dD][eE][sS][cC] ;
|
|
3066
|
-
ELEMENT : [eE][lL][eE][mM][eE][nN][tT] ;
|
|
3084
|
+
// ELEMENT : [eE][lL][eE][mM][eE][nN][tT] ;
|
|
3067
3085
|
ELEMENTS : [eE][lL][eE][mM][eE][nN][tT][sS] ;
|
|
3068
3086
|
ELSE : [eE][lL][sS][eE] ;
|
|
3069
3087
|
END : [eE][nN][dD] ;
|
|
@@ -3093,7 +3111,7 @@ LIKE : [lL][iI][kK][eE] ;
|
|
|
3093
3111
|
LIMIT : [lL][iI][mM][iI][tT] ;
|
|
3094
3112
|
LOCALIZED: [lL][oO][cC][aA][lL][iI][zZ][eE][dD];
|
|
3095
3113
|
MANY : [mM][aA][nN][yY] ;
|
|
3096
|
-
MASKED : [mM][aA][sS][kK][eE][dD] ;
|
|
3114
|
+
// MASKED : [mM][aA][sS][kK][eE][dD] ;
|
|
3097
3115
|
MINUS : [mM][iI][nN][uU][sS] ;
|
|
3098
3116
|
MIXIN : [mM][iI][xX][iI][nN] ;
|
|
3099
3117
|
NAMESPACE : [nN][aA][mM][eE][sS][pP][aA][cC][eE] ;
|