@sap/cds-compiler 2.11.2 → 2.13.6

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.
Files changed (140) hide show
  1. package/CHANGELOG.md +175 -2
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +23 -17
  5. package/bin/cdsse.js +2 -2
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +25 -6
  9. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  10. package/doc/NameResolution.md +21 -16
  11. package/lib/api/main.js +32 -79
  12. package/lib/api/options.js +3 -2
  13. package/lib/api/validate.js +2 -1
  14. package/lib/backends.js +16 -26
  15. package/lib/base/dictionaries.js +0 -8
  16. package/lib/base/error.js +26 -0
  17. package/lib/base/keywords.js +10 -19
  18. package/lib/base/location.js +9 -4
  19. package/lib/base/message-registry.js +75 -9
  20. package/lib/base/messages.js +31 -35
  21. package/lib/base/model.js +2 -62
  22. package/lib/base/optionProcessorHelper.js +246 -183
  23. package/lib/checks/.eslintrc.json +2 -0
  24. package/lib/checks/actionsFunctions.js +2 -1
  25. package/lib/checks/annotationsOData.js +1 -1
  26. package/lib/checks/cdsPersistence.js +2 -1
  27. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  28. package/lib/checks/enricher.js +17 -1
  29. package/lib/checks/foreignKeys.js +4 -4
  30. package/lib/checks/invalidTarget.js +3 -1
  31. package/lib/checks/managedInType.js +4 -4
  32. package/lib/checks/managedWithoutKeys.js +3 -1
  33. package/lib/checks/queryNoDbArtifacts.js +1 -3
  34. package/lib/checks/selectItems.js +4 -4
  35. package/lib/checks/sql-snippets.js +94 -0
  36. package/lib/checks/types.js +1 -1
  37. package/lib/checks/unknownMagic.js +1 -1
  38. package/lib/checks/validator.js +12 -7
  39. package/lib/compiler/assert-consistency.js +12 -8
  40. package/lib/compiler/base.js +0 -1
  41. package/lib/compiler/builtins.js +42 -21
  42. package/lib/compiler/checks.js +46 -12
  43. package/lib/compiler/cycle-detector.js +1 -1
  44. package/lib/compiler/define.js +1103 -0
  45. package/lib/compiler/extend.js +983 -0
  46. package/lib/compiler/finalize-parse-cdl.js +231 -0
  47. package/lib/compiler/index.js +46 -39
  48. package/lib/compiler/kick-start.js +190 -0
  49. package/lib/compiler/moduleLayers.js +4 -4
  50. package/lib/compiler/populate.js +1226 -0
  51. package/lib/compiler/propagator.js +113 -47
  52. package/lib/compiler/resolve.js +1433 -0
  53. package/lib/compiler/shared.js +100 -65
  54. package/lib/compiler/tweak-assocs.js +529 -0
  55. package/lib/compiler/utils.js +215 -33
  56. package/lib/edm/.eslintrc.json +5 -0
  57. package/lib/edm/annotations/genericTranslation.js +38 -25
  58. package/lib/edm/annotations/preprocessAnnotations.js +3 -3
  59. package/lib/edm/csn2edm.js +10 -9
  60. package/lib/edm/edm.js +19 -20
  61. package/lib/edm/edmPreprocessor.js +166 -95
  62. package/lib/edm/edmUtils.js +127 -34
  63. package/lib/gen/Dictionary.json +92 -43
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +11 -1
  66. package/lib/gen/language.tokens +86 -82
  67. package/lib/gen/languageLexer.interp +18 -1
  68. package/lib/gen/languageLexer.js +925 -847
  69. package/lib/gen/languageLexer.tokens +78 -74
  70. package/lib/gen/languageParser.js +5434 -4298
  71. package/lib/json/from-csn.js +59 -17
  72. package/lib/json/to-csn.js +189 -71
  73. package/lib/language/antlrParser.js +3 -3
  74. package/lib/language/docCommentParser.js +3 -3
  75. package/lib/language/errorStrategy.js +26 -8
  76. package/lib/language/genericAntlrParser.js +144 -53
  77. package/lib/language/language.g4 +424 -200
  78. package/lib/language/multiLineStringParser.js +536 -0
  79. package/lib/main.d.ts +550 -61
  80. package/lib/main.js +38 -11
  81. package/lib/model/api.js +3 -1
  82. package/lib/model/csnRefs.js +322 -198
  83. package/lib/model/csnUtils.js +226 -370
  84. package/lib/model/enrichCsn.js +124 -69
  85. package/lib/model/revealInternalProperties.js +29 -7
  86. package/lib/model/sortViews.js +10 -2
  87. package/lib/modelCompare/compare.js +17 -12
  88. package/lib/optionProcessor.js +8 -3
  89. package/lib/render/.eslintrc.json +1 -2
  90. package/lib/render/DuplicateChecker.js +1 -1
  91. package/lib/render/manageConstraints.js +36 -33
  92. package/lib/render/toCdl.js +174 -275
  93. package/lib/render/toHdbcds.js +203 -122
  94. package/lib/render/toRename.js +7 -10
  95. package/lib/render/toSql.js +161 -82
  96. package/lib/render/utils/common.js +22 -8
  97. package/lib/render/utils/sql.js +10 -7
  98. package/lib/render/utils/stringEscapes.js +111 -0
  99. package/lib/sql-identifier.js +1 -1
  100. package/lib/transform/.eslintrc.json +5 -0
  101. package/lib/transform/braceExpression.js +4 -2
  102. package/lib/transform/db/.eslintrc.json +2 -0
  103. package/lib/transform/db/applyTransformations.js +212 -0
  104. package/lib/transform/db/assertUnique.js +1 -1
  105. package/lib/transform/db/associations.js +187 -0
  106. package/lib/transform/db/cdsPersistence.js +150 -0
  107. package/lib/transform/db/constraints.js +61 -56
  108. package/lib/transform/db/expansion.js +50 -29
  109. package/lib/transform/db/flattening.js +556 -106
  110. package/lib/transform/db/groupByOrderBy.js +3 -1
  111. package/lib/transform/db/temporal.js +236 -0
  112. package/lib/transform/db/transformExists.js +103 -28
  113. package/lib/transform/db/views.js +92 -44
  114. package/lib/transform/draft/.eslintrc.json +38 -0
  115. package/lib/transform/{db/draft.js → draft/db.js} +9 -7
  116. package/lib/transform/draft/odata.js +227 -0
  117. package/lib/transform/forHanaNew.js +98 -783
  118. package/lib/transform/forOdataNew.js +22 -175
  119. package/lib/transform/localized.js +36 -32
  120. package/lib/transform/odata/generateForeignKeyElements.js +3 -3
  121. package/lib/transform/odata/referenceFlattener.js +95 -89
  122. package/lib/transform/odata/structureFlattener.js +1 -1
  123. package/lib/transform/odata/toFinalBaseType.js +86 -12
  124. package/lib/transform/odata/typesExposure.js +5 -5
  125. package/lib/transform/odata/utils.js +2 -2
  126. package/lib/transform/transformUtilsNew.js +47 -33
  127. package/lib/transform/translateAssocsToJoins.js +13 -30
  128. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  129. package/lib/transform/universalCsn/coreComputed.js +170 -0
  130. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  131. package/lib/transform/universalCsn/utils.js +63 -0
  132. package/lib/utils/file.js +8 -3
  133. package/lib/utils/objectUtils.js +30 -0
  134. package/lib/utils/timetrace.js +8 -2
  135. package/package.json +1 -1
  136. package/share/messages/README.md +26 -0
  137. package/lib/compiler/definer.js +0 -2349
  138. package/lib/compiler/resolver.js +0 -2922
  139. package/lib/transform/db/helpers.js +0 -58
  140. package/lib/transform/universalCsnEnricher.js +0 -67
@@ -393,7 +393,7 @@ artifactDef[ outer, defOnly = false ] locals[ annos = [] ] // cannot use `parent
393
393
  | extendProjection[ $outer, this.startLocation(), $annos ]
394
394
  | extendType[ $outer, this.startLocation(), $annos ]
395
395
  | extendAspect[ $outer, this.startLocation(), $annos ]
396
- // TODO: what about extendAction
396
+ // Streamlined Syntax
397
397
  | extendArtifact[ $outer, this.startLocation(), $annos ]
398
398
  )
399
399
  |
@@ -418,7 +418,9 @@ contextDef[ outer, loc, annos, defOnly = false ] locals[ art, name = {} ]
418
418
  this.docComment( $annos ); }
419
419
  annotationAssignment_fix[ $annos ]*
420
420
  (
421
- '{' artifactDef[ $art, defOnly, true ]* '}'
421
+ '{' { $art.artifacts = this.createDict(); }
422
+ artifactDef[ $art, defOnly ]*
423
+ '}' { this.setDictEndLocation( $art.artifacts ); }
422
424
  optionalSemi
423
425
  |
424
426
  requiredSemi
@@ -436,9 +438,9 @@ extendContext[ outer, loc, annos ] locals[ art, name = {} ]
436
438
  { this.docComment( $annos ); }
437
439
  annotationAssignment_ll1[ $annos ]*
438
440
  (
439
- '{'
440
- artifactDef[ $art, $service ? 'SERVICE' : 'CONTEXT', true ]*
441
- '}'
441
+ '{' { $art.artifacts = this.createDict(); }
442
+ artifactDef[ $art, $service ? 'SERVICE' : 'CONTEXT' ]*
443
+ '}' { this.setDictEndLocation( $art.artifacts ); }
442
444
  optionalSemi
443
445
  |
444
446
  requiredSemi
@@ -461,23 +463,35 @@ entityDef[ outer, loc, annos ] locals[ art, name = {} ]
461
463
  )*
462
464
  )?
463
465
  '{'
464
- { $art.elements = Object.create(null); } // better for include and annotate
466
+ { $art.elements = this.createDict(); } // better for include and annotate
465
467
  elementDef[ $art ]*
466
- '}'
468
+ '}' { this.setDictEndLocation( $art.elements ); }
467
469
  // TODO: action definitions in a specific section?
468
- ( ACTIONS '{' actionFunctionDef[ $art ]* '}' )?
470
+ (
471
+ ACTIONS '{' { $art.actions = this.createDict(); }
472
+ actionFunctionDef[ $art ]*
473
+ '}' { this.setDictEndLocation( $art.actions ); }
474
+ )?
469
475
  optionalSemi
470
476
  |
471
477
  AS
472
478
  ( qe=queryExpression
473
479
  { $art.query = $qe.query; $art['$'+'syntax'] = 'entity' }
474
- ( ACTIONS '{' actionFunctionDef[ $art ]* '}' optionalSemi
480
+ (
481
+ ACTIONS '{' { $art.actions = this.createDict(); }
482
+ actionFunctionDef[ $art ]*
483
+ '}' { this.setDictEndLocation( $art.actions ); }
484
+ optionalSemi
475
485
  | requiredSemi
476
486
  )
477
487
  | qp=projectionSpec
478
488
  { $art.query = $qp.query; $art['$'+'syntax'] = 'projection'; }
479
489
  projectionClauses[ $qp.query ]
480
- ( ACTIONS '{' actionFunctionDef[ $art ]* '}' )?
490
+ (
491
+ ACTIONS '{' { $art.actions = this.createDict(); }
492
+ actionFunctionDef[ $art ]*
493
+ '}' { this.setDictEndLocation( $art.actions ); }
494
+ )?
481
495
  optionalSemi // TODO: not fully correct without columns or excluding
482
496
  )
483
497
  )
@@ -490,7 +504,7 @@ projectionSpec returns[ query ] locals[ src ]
490
504
  // now a simplified `tableTerm`:
491
505
  {
492
506
  $src = { path: [], scope: 0 };
493
- $query = { op: this.tokenLocation( $proj, undefined, 'SELECT' ), from: $src, location: this.startLocation() };
507
+ $query = { op: this.valueWithTokenLocation( 'SELECT', $proj ), from: $src, location: this.startLocation() };
494
508
  }
495
509
  fromPath[ $src, 'artref']
496
510
  ( ':'
@@ -522,12 +536,12 @@ excludingClause[ query ]
522
536
  :
523
537
  // syntax is less than ideal - EXCLUDING is only useful for `*` - with
524
538
  // this syntax, people wonder what happens with explicit select items
525
- EXCLUDING '{'
539
+ EXCLUDING '{' { $query.excludingDict = this.createDict(); }
526
540
  projectionExclusion[ $query ]
527
541
  ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
528
542
  projectionExclusion[ $query ]
529
543
  )*
530
- '}'
544
+ '}' { this.setDictEndLocation( $query.excludingDict ); }
531
545
  ;
532
546
 
533
547
  projectionExclusion[ outer ] locals[ art ]
@@ -562,13 +576,19 @@ extendEntity[ outer, loc, annos ] locals[ art, name = {} ]
562
576
 
563
577
  extendForEntity[ art ]
564
578
  :
565
- '{'
579
+ '{' { $art.elements = this.createDict(); }
566
580
  elementDefOrExtend[ $art ]*
567
- '}'
568
- ( ACTIONS '{' actionFunctionDef[ $art ]* '}' )?
581
+ '}' { this.setDictEndLocation( $art.elements ); }
582
+ (
583
+ ACTIONS '{' { $art.actions = this.createDict(); }
584
+ actionFunctionDef[ $art ]*
585
+ '}' { this.setDictEndLocation( $art.actions ); }
586
+ )?
569
587
  optionalSemi
570
588
  |
571
- ACTIONS '{' actionFunctionDef[ $art ]* '}'
589
+ ACTIONS '{' { $art.actions = this.createDict(); }
590
+ actionFunctionDef[ $art ]*
591
+ '}' { this.setDictEndLocation( $art.actions ); }
572
592
  optionalSemi
573
593
  |
574
594
  requiredSemi
@@ -594,10 +614,16 @@ extendProjection[ outer, loc, annos ] locals[ art, name = {} ]
594
614
  )*
595
615
  )?
596
616
  '}'
597
- ( ACTIONS '{' actionFunctionDef[ $art ]* '}' )?
617
+ (
618
+ ACTIONS '{' { $art.actions = this.createDict(); }
619
+ actionFunctionDef[ $art ]*
620
+ '}' { this.setDictEndLocation( $art.actions ); }
621
+ )?
598
622
  optionalSemi
599
623
  |
600
- ACTIONS '{' actionFunctionDef[ $art ]* '}'
624
+ ACTIONS '{' { $art.actions = this.createDict(); }
625
+ actionFunctionDef[ $art ]*
626
+ '}' { this.setDictEndLocation( $art.actions ); }
601
627
  optionalSemi
602
628
  |
603
629
  requiredSemi
@@ -702,12 +728,15 @@ aspectDef[ outer, loc, annos ] locals[ art, name = {} ]
702
728
  )*
703
729
  )?
704
730
  )?
705
- '{'
706
- { $art.elements = Object.create(null); } // better for include and annotate
731
+ '{' { $art.elements = this.createDict(); }
707
732
  ( elementDef[ $art ]* )
708
- '}'
733
+ '}' { this.setDictEndLocation( $art.elements ); }
709
734
  // TODO: action definitions in a specific section?
710
- ( ACTIONS '{' actionFunctionDef[ $art ]* '}' )?
735
+ (
736
+ ACTIONS '{' { $art.actions = this.createDict(); }
737
+ actionFunctionDef[ $art ]*
738
+ '}' { this.setDictEndLocation( $art.actions ); }
739
+ )?
711
740
  optionalSemi
712
741
  ;
713
742
 
@@ -758,13 +787,82 @@ annotationDef[ outer, loc, annos ] locals[ art, name = {} ]
758
787
  typeSpecSemi[ $art, $annos ] // also 'includes'...
759
788
  ;
760
789
 
761
- extendArtifact[ outer, loc, annos ] locals[ art, name = {} ]
762
- @after { this.attachLocation($art); }
790
+ extendArtifact[ outer, loc, annos ] locals[ art, name = {}, elemName = {} ]
791
+ @after{ /* #ATN 1 */ this.attachLocation($art); }
763
792
  :
764
793
  simplePath[ $name, 'Extend' ]
765
- { $art = this.addItem( $outer, 'extensions', 'extend', $annos,
766
- { name: $name }, $loc ); }
767
- extendWithOptElements[ $art, $annos ]
794
+ (
795
+ { $art = this.addItem( $outer, 'extensions', 'extend', $annos, { name: $name }, $loc ); }
796
+ |
797
+ ':'
798
+ simplePath[ $elemName, 'ref']
799
+ {{
800
+ const def = this.addItem( $outer, 'extensions', 'extend', null, { name: $name }, $loc );
801
+ $art = this.artifactForElementAnnotateOrExtend( 'extend', def, $elemName.path, $annos, $loc );
802
+ }}
803
+ )
804
+ (
805
+ { this.docComment( $annos ); }
806
+ annotationAssignment_ll1[ $annos ]*
807
+ (
808
+ '{' { $art.elements = this.createDict(); }
809
+ elementDefOrExtend[ $art ]*
810
+ '}' { this.setDictEndLocation( $art.elements ); }
811
+ optionalSemi
812
+ |
813
+ requiredSemi
814
+ )
815
+ |
816
+ WITH { this.noSemicolonHere(); this.docComment( $annos ); }
817
+ annotationAssignment_ll1[ $annos ]*
818
+ // #ATN: DEFINITIONS, COLUMNS, ACTIONS etc are not reserved and could be identifiers (ref).
819
+ (
820
+ includeRef[ $art ]
821
+ requiredSemi
822
+ |
823
+ '{' { $art.elements = this.createDict(); }
824
+ elementDefOrExtend[ $art ]*
825
+ '}' { this.setDictEndLocation( $art.elements ); }
826
+ optionalSemi
827
+ |
828
+ requiredSemi
829
+ |
830
+ { this.disallowElementExtension( $elemName, $outer, 'definitions' ); }
831
+ DEFINITIONS
832
+ '{' { $art.artifacts = this.createDict(); }
833
+ artifactDef[ $art, true ]*
834
+ '}' { this.setDictEndLocation( $art.artifacts ); }
835
+ optionalSemi
836
+ |
837
+ { this.disallowElementExtension( $elemName, $outer, 'columns' ); }
838
+ COLUMNS
839
+ '{' { $art.columns = []; }
840
+ (
841
+ selectItemDef[ $art.columns ]
842
+ ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
843
+ selectItemDef[ $art.columns ]
844
+ )*
845
+ )?
846
+ '}'
847
+ optionalSemi
848
+ |
849
+ { this.disallowElementExtension( $elemName, $outer, 'actions' ); }
850
+ ACTIONS '{' { $art.actions = this.createDict(); }
851
+ actionFunctionDef[ $art ]*
852
+ '}' { this.setDictEndLocation( $art.actions ); }
853
+ optionalSemi
854
+ |
855
+ ELEMENTS '{' { $art.elements = this.createDict(); }
856
+ elementDefOrExtend[ $art ]*
857
+ '}' { this.setDictEndLocation( $art.elements ); }
858
+ optionalSemi
859
+ |
860
+ ENUM '{' { $art.enum = this.createDict(); }
861
+ enumSymbolDef[ $art ]*
862
+ '}' { this.setDictEndLocation( $art.enum ); }
863
+ optionalSemi
864
+ )
865
+ )
768
866
  ;
769
867
 
770
868
  extendWithOptElements[ art, annos ]
@@ -775,9 +873,9 @@ extendWithOptElements[ art, annos ]
775
873
  includeRef[ $art ]
776
874
  requiredSemi
777
875
  |
778
- '{'
876
+ '{' { $art.elements = this.createDict(); }
779
877
  elementDefOrExtend[ $art ]*
780
- '}'
878
+ '}' { this.setDictEndLocation( $art.elements ); }
781
879
  optionalSemi
782
880
  |
783
881
  requiredSemi
@@ -786,59 +884,71 @@ extendWithOptElements[ art, annos ]
786
884
  { this.docComment( $annos ); }
787
885
  annotationAssignment_ll1[ $annos ]*
788
886
  (
789
- '{'
887
+ '{' { $art.elements = this.createDict(); }
790
888
  elementDefOrExtend[ $art ]*
791
- '}'
889
+ '}' { this.setDictEndLocation( $art.elements ); }
792
890
  optionalSemi
793
891
  |
794
892
  requiredSemi
795
893
  )
796
894
  ;
797
895
 
798
- annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
896
+ annotateArtifact[ outer, loc, annos ] locals[ art, name = {}, elemName = {} ]
799
897
  @after { this.attachLocation($art); }
800
898
  :
801
899
  simplePath[ $name, 'Annotate' ]
802
- { $art = this.addItem( $outer, 'extensions', 'annotate', $annos, { name: $name }, $loc ); }
900
+ (
901
+ { $art = this.addItem( $outer, 'extensions', 'annotate', $annos, { name: $name }, $loc ); }
902
+ |
903
+ ':'
904
+ simplePath[ $elemName, 'ref']
905
+ {{
906
+ const def = this.addItem( $outer, 'extensions', 'annotate', null, { name: $name }, $loc );
907
+ $art = this.artifactForElementAnnotateOrExtend( 'annotate', def, $elemName.path, $annos, $loc );
908
+ }}
909
+ )
910
+
803
911
  ( WITH { this.noSemicolonHere(); } )?
804
912
  { this.docComment( $annos ); }
805
913
  annotationAssignment_ll1[ $annos ]*
806
914
  (
807
- '{'
915
+ '{' { $art.elements = this.createDict(); }
808
916
  annotateElement[ $art ]*
809
- '}'
917
+ '}' { this.setDictEndLocation( $art.elements ); }
810
918
  (
811
919
  ACTIONS
812
- '{'
920
+ '{' { $art.actions = this.createDict(); }
813
921
  annotateAction[ $art ]*
814
- '}'
922
+ '}' { this.setDictEndLocation( $art.actions ); }
815
923
  )?
816
924
  optionalSemi
817
925
  |
818
926
  ACTIONS
819
- '{'
927
+ '{' { $art.actions = this.createDict(); }
820
928
  annotateAction[ $art ]*
821
- '}'
929
+ '}' { this.setDictEndLocation( $art.actions ); }
822
930
  optionalSemi
823
931
  |
824
- '('
932
+ '(' { $art.params = this.createDict(); }
825
933
  annotateParam[ $art ]
826
934
  ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
827
935
  annotateParam[ $art ]
828
936
  )*
829
- ')'
937
+ ')' { this.setDictEndLocation( $art.params ); }
830
938
  (
831
- RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
939
+ RETURNS { $art['$'+'syntax'] = 'returns'; }
940
+ '{' { $art.elements = this.createDict(); }
832
941
  annotateElement[ $art ]*
833
- '}'
942
+ '}' { this.setDictEndLocation( $art.elements ); }
834
943
  optionalSemi
835
944
  |
836
945
  requiredSemi
837
946
  )
838
947
  |
839
- RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
948
+ RETURNS { $art['$'+'syntax'] = 'returns'; }
949
+ '{' { $art.elements = this.createDict(); }
840
950
  annotateElement[ $art ]*
841
- '}'
951
+ '}' { this.setDictEndLocation( $art.elements ); }
842
952
  optionalSemi
843
953
 
844
954
  |
@@ -856,9 +966,9 @@ annotateElement[ outer ] locals[ art, annos = [] ]
856
966
  this.docComment( $annos ); }
857
967
  annotationAssignment_ll1[ $annos ]*
858
968
  (
859
- '{'
969
+ '{' { $art.elements = this.createDict(); }
860
970
  annotateElement[ $art ]*
861
- '}'
971
+ '}' { this.setDictEndLocation( $art.elements ); }
862
972
  optionalSemi
863
973
  |
864
974
  requiredSemi
@@ -875,17 +985,17 @@ annotateAction [ outer ] locals [ art, annos = [] ]
875
985
  this.docComment( $annos ); }
876
986
  annotationAssignment_ll1[ $annos ]*
877
987
  (
878
- '('
988
+ '(' { $art.params = this.createDict(); }
879
989
  annotateParam[ $art ]
880
990
  ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
881
991
  annotateParam[ $art ]
882
992
  )*
883
- ')'
993
+ ')' { this.setDictEndLocation( $art.params ); }
884
994
  )?
885
995
  (
886
- RETURNS '{'
996
+ RETURNS '{' { $art.elements = this.createDict(); }
887
997
  annotateElement[ $art ]*
888
- '}'
998
+ '}' { this.setDictEndLocation( $art.elements ); }
889
999
  optionalSemi
890
1000
  |
891
1001
  requiredSemi
@@ -929,6 +1039,12 @@ enumSymbolDef[ outer ] locals[ art, annos = [] ]
929
1039
  requiredSemi
930
1040
  ;
931
1041
 
1042
+ defaultValue[ art ] locals[ elem, elements = {} ]
1043
+ :
1044
+ // TODO: We may support structured default values here.
1045
+ DEFAULT expr=expression { $art.default = $expr.expr; }
1046
+ ;
1047
+
932
1048
  elementDefOrExtend[ outer ] locals[ annos = [] ]
933
1049
  @after { /* #ATN 1 */ if ($ctx.art) this.attachLocation($art.art); }
934
1050
  // tool complains if I test for ($art)
@@ -983,6 +1099,7 @@ mixinElementDef[ outer ] locals[ art ]
983
1099
 
984
1100
  misplacedAnnotations[ annos, messageId ]
985
1101
  :
1102
+ // No docComment() here
986
1103
  annotationAssignment_ll1[ $annos ]+
987
1104
  { if ($messageId) // issue specified in central registry
988
1105
  this.message( messageId, this.tokenLocation( $ctx.start, this.getCurrentToken() ) );
@@ -994,7 +1111,7 @@ elementDefInner[ outer, loc, annos, allowEq ] returns[ art ]
994
1111
  :
995
1112
  // TODO: it would be excellent to remove ELEMENT...
996
1113
  // or have a special ident rule without the ELEMENT
997
- // Reason: it would be good for error recover to start a major block without LL1 ambiguity
1114
+ // Reason: it would be good for error recovery to start a major block without LL1 ambiguity
998
1115
  // VIRTUAL is keyword, except if before the following tokens texts:
999
1116
  { this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[:{@=}]$/ ); }
1000
1117
  virtual=VIRTUAL? key=KEY?
@@ -1046,7 +1163,7 @@ elementDefInner[ outer, loc, annos, allowEq ] returns[ art ]
1046
1163
  )
1047
1164
  |
1048
1165
  (
1049
- array=ARRAY of=OF
1166
+ array=ARRAY of=OF
1050
1167
  { $art.items = { location: this.tokenLocation( $array, $of ) }; }
1051
1168
  | many=MANY
1052
1169
  { $art.items = { location: this.tokenLocation( $many ) };}
@@ -1064,10 +1181,9 @@ elementDefInner[ outer, loc, annos, allowEq ] returns[ art ]
1064
1181
  { this.docComment( $annos ); }
1065
1182
  annotationAssignment_ll1[ $annos ]*
1066
1183
  (
1067
- ENUM '{'
1068
- { $art.items.enum = Object.create(null); }
1069
- enumSymbolDef[ $art.items ]*
1070
- '}'
1184
+ ENUM '{' { $art.items.enum = this.createDict(); }
1185
+ enumSymbolDef[ $art.items ]*
1186
+ '}' { this.setDictEndLocation( $art.items.enum ); }
1071
1187
  misplacedAnnotations[ $annos, 'syntax-anno-after-enum' ]?
1072
1188
  )?
1073
1189
  )
@@ -1078,7 +1194,7 @@ elementDefInner[ outer, loc, annos, allowEq ] returns[ art ]
1078
1194
  annotationAssignment_ll1[ $annos ]*
1079
1195
  requiredSemi // also req after foreign key spec
1080
1196
  |
1081
- l=LOCALIZED { $art.localized = this.tokenLocation( $l, undefined, true ); }
1197
+ l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1082
1198
  typeRefOptArgs[ $art ]
1083
1199
  { this.docComment( $annos ); }
1084
1200
  annotationAssignment_ll1[ $annos ]*
@@ -1091,13 +1207,14 @@ elementDefInner[ outer, loc, annos, allowEq ] returns[ art ]
1091
1207
  typeRefOptArgs[ $art ]
1092
1208
  { this.docComment( $annos ); }
1093
1209
  annotationAssignment_ll1[ $annos ]*
1094
- ( ENUM '{'
1095
- { $art.enum = Object.create(null); }
1096
- enumSymbolDef[ $art ]*
1097
- '}'
1210
+ (
1211
+ ENUM '{' { $art.enum = this.createDict(); }
1212
+ enumSymbolDef[ $art ]*
1213
+ '}' { this.setDictEndLocation( $art.enum ); }
1098
1214
  elementProperties[ $art ]?
1099
1215
  misplacedAnnotations[ $annos, 'syntax-anno-after-enum' ]?
1100
- | elementProperties[ $art ]
1216
+ |
1217
+ elementProperties[ $art ]
1101
1218
  { this.docComment( $annos ); }
1102
1219
  annotationAssignment_ll1[ $annos ]*
1103
1220
  )?
@@ -1146,7 +1263,7 @@ selectItemDef[ outer ] locals[ annos = [] ]
1146
1263
  @after{ if ($ctx.art) this.attachLocation($art.art); }
1147
1264
  :
1148
1265
  star='*'
1149
- { $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
1266
+ { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1150
1267
  |
1151
1268
  { this.docComment( $annos ); }
1152
1269
  annotationAssignment_atn[ $annos ]*
@@ -1156,8 +1273,8 @@ selectItemDef[ outer ] locals[ annos = [] ]
1156
1273
  key=KEY?
1157
1274
  art=selectItemDefBody[ $outer, $annos ]
1158
1275
  {
1159
- if ($virtual) $art.art.virtual = this.tokenLocation( $virtual, undefined, true );
1160
- if ($key) $art.art.key = this.tokenLocation( $key, undefined, true );
1276
+ if ($virtual) $art.art.virtual = this.valueWithTokenLocation( true, $virtual );
1277
+ if ($key) $art.art.key = this.valueWithTokenLocation( true, $key );
1161
1278
  }
1162
1279
  ;
1163
1280
 
@@ -1190,7 +1307,7 @@ selectItemDefBody[ outer, annos ] returns[ art = {} ]
1190
1307
  excludingClause[ $art ]?
1191
1308
  |
1192
1309
  star='*'
1193
- { $art.inline = [ this.tokenLocation( $star, undefined, '*' ) ]; }
1310
+ { $art.inline = [ this.valueWithTokenLocation( '*', $star ) ]; }
1194
1311
  )
1195
1312
  )?
1196
1313
  |
@@ -1239,7 +1356,7 @@ selectItemInlineDef[ outer ] locals[ annos = [] ]
1239
1356
  @after{ if ($ctx.art) this.attachLocation($art.art); }
1240
1357
  :
1241
1358
  star='*'
1242
- { $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
1359
+ { $outer.push( this.valueWithTokenLocation( '*', $star ) ); }
1243
1360
  |
1244
1361
  { this.docComment( $annos ); }
1245
1362
  annotationAssignment_atn[ $annos ]*
@@ -1248,7 +1365,7 @@ selectItemInlineDef[ outer ] locals[ annos = [] ]
1248
1365
 
1249
1366
  parameterListDef[ art ]
1250
1367
  :
1251
- '('
1368
+ '(' { $art.params = this.createDict(); }
1252
1369
  // also empty param list (we might do some hacking later to allow reserved words)
1253
1370
  // see annotationAssignment_paren
1254
1371
  (
@@ -1257,7 +1374,7 @@ parameterListDef[ art ]
1257
1374
  parameterDef[ $art ]
1258
1375
  )*
1259
1376
  )?
1260
- ')'
1377
+ ')' { this.setDictEndLocation( $art.params ); }
1261
1378
  ;
1262
1379
 
1263
1380
  parameterDef[ outer ] locals[ art, annos = [] ]
@@ -1270,14 +1387,14 @@ parameterDef[ outer ] locals[ art, annos = [] ]
1270
1387
  this.docComment( $annos ); }
1271
1388
  annotationAssignment_fix[ $annos ]*
1272
1389
  typeSpec[ $art ]
1273
- ( DEFAULT expr=expression { $art.default = $expr.expr; } )?
1390
+ defaultValue[ $art ]?
1274
1391
  { this.docComment( $annos ); }
1275
1392
  annotationAssignment_ll1[ $annos ]*
1276
1393
  ;
1277
1394
 
1278
1395
  entityParameters[ art ]
1279
1396
  :
1280
- '('
1397
+ '(' { $art.params = this.createDict(); }
1281
1398
  // also empty param list (we might do some hacking later to allow reserved words)
1282
1399
  // see annotationAssignment_paren
1283
1400
  (
@@ -1286,7 +1403,7 @@ entityParameters[ art ]
1286
1403
  entityParameterDef[ $art ]
1287
1404
  )*
1288
1405
  )?
1289
- ')'
1406
+ ')' { this.setDictEndLocation( $art.params ); }
1290
1407
  ;
1291
1408
 
1292
1409
  entityParameterDef[ outer ] locals[ art, annos = [] ]
@@ -1299,31 +1416,27 @@ entityParameterDef[ outer ] locals[ art, annos = [] ]
1299
1416
  this.docComment( $annos ); }
1300
1417
  annotationAssignment_fix[ $annos ]*
1301
1418
  typeSpec[ $art ]
1302
- ( DEFAULT expr=expression { $art.default = $expr.expr; } )?
1419
+ defaultValue[ $art ]?
1420
+ { this.docComment( $annos ); }
1421
+ annotationAssignment_ll1[ $annos ]*
1303
1422
  ;
1304
1423
 
1305
1424
  nullability[ art ]
1306
1425
  :
1307
1426
  not=NOT n1=NULL
1308
- { $art.notNull = this.tokenLocation($not,$n1,true); }
1427
+ { $art.notNull = this.valueWithTokenLocation( true, $not, $n1 ); }
1309
1428
  |
1310
1429
  n2=NULL
1311
- { $art.notNull = this.tokenLocation($n2,undefined,false); }
1430
+ { $art.notNull = this.valueWithTokenLocation( false, $n2 ); }
1312
1431
  ;
1313
1432
 
1314
1433
  elementProperties[ elem ]
1315
1434
  :
1316
- nullability[$elem]
1317
- (
1318
- DEFAULT expr=expression
1319
- { $elem.default = $expr.expr; }
1320
- )?
1435
+ nullability[ $elem ]
1436
+ defaultValue[ $elem ]?
1321
1437
  |
1322
- (
1323
- DEFAULT expr=expression
1324
- { $elem.default = $expr.expr; }
1325
- )
1326
- nullability[$elem]?
1438
+ defaultValue[ $elem ]
1439
+ nullability[ $elem ]?
1327
1440
  |
1328
1441
  eq='='
1329
1442
  { this.notSupportedYet( 'Calculated fields are not supported yet', $eq ); }
@@ -1374,10 +1487,10 @@ typeSpec[ art ] // for params
1374
1487
  // TODO: no LOCALIZED ?
1375
1488
  | typeRefOptArgs[ $art ]
1376
1489
  nullability[ $art ]?
1377
- ( ENUM '{'
1378
- { $art.enum = Object.create(null); }
1379
- enumSymbolDef[ $art ]*
1380
- '}'
1490
+ (
1491
+ ENUM '{' { $art.enum = this.createDict(); }
1492
+ enumSymbolDef[ $art ]*
1493
+ '}' { this.setDictEndLocation( $art.enum ); }
1381
1494
  )?
1382
1495
  )
1383
1496
  ;
@@ -1387,7 +1500,7 @@ returnTypeSpec[ art, annos ]
1387
1500
  :
1388
1501
  ret=RETURNS { $art.returns = { location: this.tokenLocation( $ret ), kind: 'param' }; }
1389
1502
  // #ATN: typeSimple can start with ARRAY or TYPE
1390
- ( typeStruct[ $art.returns ]
1503
+ ( typeStruct[ $art.returns ]
1391
1504
  nullability[ $art.returns ]?
1392
1505
  | typeArray[ $art.returns ] // nullability is set in typeArray
1393
1506
  | typeTypeOf[ $art.returns ]
@@ -1395,11 +1508,12 @@ returnTypeSpec[ art, annos ]
1395
1508
  // TODO: no LOCALIZED ?
1396
1509
  | typeRefOptArgs[ $art.returns ]
1397
1510
  nullability[ $art.returns ]?
1398
- ( ENUM '{'
1399
- { $art.returns.enum = Object.create(null); }
1400
- enumSymbolDef[ $art.returns ]*
1401
- '}'
1402
- | misplacedAnnotations[ $annos, 'syntax-anno-after-params' ]
1511
+ (
1512
+ ENUM '{' { $art.returns.enum = this.createDict(); }
1513
+ enumSymbolDef[ $art.returns ]*
1514
+ '}' { this.setDictEndLocation( $art.returns.enum ); }
1515
+ |
1516
+ misplacedAnnotations[ $annos, 'syntax-anno-after-params' ]
1403
1517
  )?
1404
1518
  )
1405
1519
 
@@ -1440,7 +1554,7 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1440
1554
  { $art.items = { location: this.tokenLocation( $many ) };}
1441
1555
  )
1442
1556
  // #ATN: typeRefOptArgs can start with TYPE
1443
- ( typeStruct[ $art.items ]
1557
+ ( typeStruct[ $art.items ]
1444
1558
  nullability[ $art.items ]?
1445
1559
  optionalSemi
1446
1560
  | typeTypeOf[ $art.items ]
@@ -1452,21 +1566,24 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1452
1566
  nullability[ $art.items ]?
1453
1567
  { this.docComment( $annos ); }
1454
1568
  annotationAssignment_ll1[ $annos ]*
1455
- ( ENUM '{'
1456
- { $art.items.enum = Object.create(null); }
1457
- enumSymbolDef[ $art.items ]*
1458
- '}'
1459
- optionalSemi
1460
- | requiredSemi
1569
+ (
1570
+ ENUM '{' { $art.items.enum = this.createDict(); }
1571
+ enumSymbolDef[ $art.items ]*
1572
+ '}' { this.setDictEndLocation( $art.items.enum ); }
1573
+ optionalSemi
1574
+ |
1575
+ requiredSemi
1461
1576
  )
1462
1577
  )
1463
1578
  |
1464
1579
  typeTypeOf[ $art ]
1580
+ defaultValue[ $art ]?
1465
1581
  { this.docComment( $annos ); }
1466
1582
  annotationAssignment_ll1[ $annos ]* requiredSemi
1467
1583
  |
1468
- l=LOCALIZED { $art.localized = this.tokenLocation( $l, undefined, true ); }
1584
+ l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1469
1585
  typeRefOptArgs[ $art ]
1586
+ defaultValue[ $art ]?
1470
1587
  { this.docComment( $annos ); }
1471
1588
  annotationAssignment_ll1[ $annos ]*
1472
1589
  requiredSemi
@@ -1497,11 +1614,19 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1497
1614
  ')'
1498
1615
  { this.docComment( $annos ); }
1499
1616
  annotationAssignment_ll1[ $annos ]*
1500
- ( ENUM '{'
1501
- { $art.enum = Object.create(null); }
1502
- enumSymbolDef[ $art ]*
1503
- '}'
1504
- optionalSemi | requiredSemi
1617
+ (
1618
+ ENUM '{' { $art.enum = this.createDict(); }
1619
+ enumSymbolDef[ $art ]*
1620
+ '}' { this.setDictEndLocation( $art.enum ); }
1621
+ (
1622
+ optionalSemi
1623
+ |
1624
+ defaultValue[ $art ]
1625
+ requiredSemi
1626
+ )
1627
+ |
1628
+ defaultValue[ $art ]?
1629
+ requiredSemi
1505
1630
  )
1506
1631
  |
1507
1632
  ':' // with element, e.g. `type T : E:elem enum { ... }`
@@ -1509,20 +1634,36 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1509
1634
  simplePath[ $art.type, 'ref']
1510
1635
  { this.docComment( $annos ); }
1511
1636
  annotationAssignment_ll1[ $annos ]*
1512
- ( ENUM '{'
1513
- { $art.enum = Object.create(null); }
1514
- enumSymbolDef[ $art ]*
1515
- '}'
1516
- optionalSemi | requiredSemi
1637
+ (
1638
+ ENUM '{' { $art.enum = this.createDict(); }
1639
+ enumSymbolDef[ $art ]*
1640
+ '}' { this.setDictEndLocation( $art.enum ); }
1641
+ (
1642
+ optionalSemi
1643
+ |
1644
+ defaultValue[ $art ]
1645
+ requiredSemi
1646
+ )
1647
+ |
1648
+ defaultValue[ $art ]?
1649
+ requiredSemi
1517
1650
  )
1518
1651
  |
1519
1652
  { this.docComment( $annos ); }
1520
1653
  annotationAssignment_ll1[ $annos ]*
1521
- ( ENUM '{'
1522
- { $art.enum = Object.create(null); }
1523
- enumSymbolDef[ $art ]*
1524
- '}'
1525
- optionalSemi | requiredSemi
1654
+ (
1655
+ ENUM '{' { $art.enum = this.createDict(); }
1656
+ enumSymbolDef[ $art ]*
1657
+ '}' { this.setDictEndLocation( $art.enum ); }
1658
+ (
1659
+ optionalSemi
1660
+ |
1661
+ defaultValue[ $art ]
1662
+ requiredSemi
1663
+ )
1664
+ |
1665
+ defaultValue[ $art ]?
1666
+ requiredSemi
1526
1667
  )
1527
1668
  |
1528
1669
  // TODO: complain if used in anno def?
@@ -1539,15 +1680,17 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1539
1680
  typeStruct[ art, attachLoc = false ]
1540
1681
  @after { if ($attachLoc) this.attachLocation($art); }
1541
1682
  :
1542
- { $art.elements = Object.create(null); } // we allow empty structures
1543
- '{' elementDef[ $art ]* '}'
1683
+ '{' { $art.elements = this.createDict(); }
1684
+ elementDef[ $art ]*
1685
+ '}' { this.setDictEndLocation( $art.elements ); }
1544
1686
  ;
1545
1687
 
1546
1688
  typeCompoStruct[ art ]
1547
1689
  @after { this.attachLocation($art); }
1548
1690
  :
1549
- { $art.elements = Object.create(null); } // we allow empty structures
1550
- COMPOSITIONofBRACE elementDef[ $art ]* '}'
1691
+ COMPOSITIONofBRACE { $art.elements = this.createDict(); }
1692
+ elementDef[ $art ]*
1693
+ '}' { this.setDictEndLocation( $art.elements ); }
1551
1694
  ;
1552
1695
 
1553
1696
  typeArray[ art ]
@@ -1566,10 +1709,10 @@ typeArray[ art ]
1566
1709
  nullability[ $art.items ]?
1567
1710
  | typeRefOptArgs[ $art.items ]
1568
1711
  nullability[ $art.items ]?
1569
- ( ENUM '{'
1570
- { $art.items.enum = Object.create(null); }
1571
- enumSymbolDef[ $art.items ]*
1572
- '}'
1712
+ (
1713
+ ENUM '{' { $art.items.enum = this.createDict(); }
1714
+ enumSymbolDef[ $art.items ]*
1715
+ '}' { this.setDictEndLocation( $art.items.enum ); }
1573
1716
  )?
1574
1717
  )
1575
1718
  ;
@@ -1597,7 +1740,7 @@ typeAssociationBase[ art, handleTypeCompo ] // including Composition
1597
1740
  typeAssociationCont[ art ]
1598
1741
  :
1599
1742
  (
1600
- '{'
1743
+ '{' { $art.foreignKeys = this.createDict(); }
1601
1744
  { this.addDef( $art, 'foreignKeys' ); }
1602
1745
  (
1603
1746
  foreignKey[ $art ]
@@ -1605,7 +1748,7 @@ typeAssociationCont[ art ]
1605
1748
  foreignKey[ $art ]
1606
1749
  )*
1607
1750
  )?
1608
- '}'
1751
+ '}' { this.setDictEndLocation( $art.foreignKeys ); }
1609
1752
  |
1610
1753
  ON cond=condition
1611
1754
  { $art.on=$cond.cond; }
@@ -1616,7 +1759,7 @@ typeAssociationElementCont[ art, annos ] // including Composition
1616
1759
  // optional NULL / NOT NULL for managed association only
1617
1760
  :
1618
1761
  (
1619
- '{'
1762
+ '{' { $art.foreignKeys = this.createDict(); }
1620
1763
  { this.addDef( $art, 'foreignKeys' ); }
1621
1764
  (
1622
1765
  foreignKey[ $art ]
@@ -1624,7 +1767,7 @@ typeAssociationElementCont[ art, annos ] // including Composition
1624
1767
  foreignKey[ $art ]
1625
1768
  )*
1626
1769
  )?
1627
- '}'
1770
+ '}' { this.setDictEndLocation( $art.foreignKeys ); }
1628
1771
  nullability[ $art ]?
1629
1772
  |
1630
1773
  ON cond=condition
@@ -1764,33 +1907,47 @@ orderByClause[ inQuery ] returns [ query ]
1764
1907
  ( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
1765
1908
  ;
1766
1909
 
1910
+ // Generic function ORDER BY clause, e.g. `first_value(id order by name)`
1911
+ // lhsExpr is the left expression of the ORDER BY clause.
1912
+ functionOrderByClause[ lhsExpr ] returns [ expr ]
1913
+ @after { this.attachLocation( $expr ); }
1914
+ :
1915
+ o=ORDER b=BY { $expr = { op: this.valueWithTokenLocation( 'orderBy', $o, $b ) , args: [ $lhsExpr ] }}
1916
+ ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
1917
+ ( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
1918
+ ;
1919
+
1767
1920
  overOrderByClause returns [ expr ]
1921
+ @after { this.attachLocation( $expr ); }
1768
1922
  :
1769
- o=ORDER b=BY { $expr = { op: this.tokenLocation($o, $b, 'orderBy' ) , args: [] }}
1923
+ o=ORDER b=BY { $expr = { op: this.valueWithTokenLocation( 'overOrderBy', $o, $b ) , args: [] }}
1770
1924
  ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
1771
1925
  ( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
1772
1926
  ;
1773
1927
 
1774
1928
  partitionByClause returns [ expr ]
1929
+ @after { this.attachLocation( $expr ); }
1775
1930
  :
1776
- p=PARTITION b=BY { $expr = { op: this.tokenLocation($p, $b, 'partitionBy' ) , args: [] }}
1931
+ p=PARTITION b=BY { $expr = { op: this.valueWithTokenLocation( 'partitionBy', $p, $b ) , args: [] }}
1777
1932
  e1=expression { $expr.args.push( $e1.expr ); }
1778
1933
  ( ',' en=expression { $expr.args.push( $en.expr ); } )*
1779
1934
  ;
1780
1935
 
1781
1936
  windowFrameClause returns [ wf ]
1937
+ @after { this.attachLocation( $wf ); }
1782
1938
  :
1783
- r=ROWS { $wf = { op: this.tokenLocation($r, null, 'rows' ) , args: [] }}
1939
+ r=ROWS { $wf = { op: this.valueWithTokenLocation( 'rows', $r ) , args: [] }}
1784
1940
  wfe=windowFrameExtentSpec { $wf.args.push( $wfe.wfe ); }
1785
1941
  ;
1786
1942
 
1787
1943
  windowFrameExtentSpec returns[ wfe ]
1944
+ @after { this.attachLocation( $wfe ); }
1788
1945
  :
1789
1946
  { $wfe = {} }
1790
1947
  windowFrameStartSpec [ $wfe ]
1791
1948
  |
1792
1949
  b=BETWEEN
1793
- { $wfe = { op: this.tokenLocation( $b, null, 'frameBetween' ), args: [] } }
1950
+ { $wfe = { op: this.valueWithTokenLocation( 'frameBetween', $b ), args: [] } }
1794
1951
  wfb1=windowFrameBoundSpec { $wfe.args.push( $wfb1.wfb ); }
1795
1952
  AND
1796
1953
  wfb2=windowFrameBoundSpec { $wfe.args.push( $wfb2.wfb ); }
@@ -1802,42 +1959,43 @@ windowFrameBoundSpec returns [ wfb ]
1802
1959
  // #ATN: Not ll1 because `UNBOUNDED` could also be part of the windowFrameStartSpec
1803
1960
  // `UNBOUNDED` would then be immediately followed by `PRECEDING`
1804
1961
  u=UNBOUNDED f=FOLLOWING
1805
- { $wfb = { op: this.tokenLocation($u, $f, 'unboundedFollowing' ), args: []} }
1962
+ { $wfb = { op: this.valueWithTokenLocation( 'unboundedFollowing', $u, $f ), args: []} }
1806
1963
  |
1807
1964
  // #ATN: Not ll1 because `Number` could also be part of the windowFrameStartSpec
1808
1965
  // `Number` would then be immediately followed by `PRECEDING`
1809
1966
  n=Number f=FOLLOWING
1810
- { $wfb = { op: this.tokenLocation($n, $f, 'following' ), args: [ this.numberLiteral( $n ) ]} }
1967
+ { $wfb = { op: this.valueWithTokenLocation( 'following', $n, $f ), args: [ this.numberLiteral( $n ) ]} }
1811
1968
  |
1812
1969
  { $wfb = {} }
1813
1970
  windowFrameStartSpec [ $wfb ]
1814
1971
  ;
1815
1972
 
1816
1973
  windowFrameStartSpec [ wf ]
1974
+ @after { this.attachLocation( $wf ); }
1817
1975
  :
1818
1976
  u=UNBOUNDED p=PRECEDING
1819
1977
  {
1820
- $wf.op = this.tokenLocation($u, $p, 'unboundedPreceding' );
1978
+ $wf.op = this.valueWithTokenLocation( 'unboundedPreceding', $u, $p );
1821
1979
  $wf.args = [];
1822
1980
  }
1823
1981
  |
1824
1982
  n=Number p=PRECEDING
1825
1983
  {
1826
- $wf.op = this.tokenLocation($p, null, 'preceding' );
1984
+ $wf.op = this.valueWithTokenLocation( 'preceding', $p );
1827
1985
  $wf.args = [ this.numberLiteral( $n ) ];
1828
1986
  }
1829
- |
1987
+ |
1830
1988
  c=CURRENT r=ROW
1831
1989
  {
1832
- $wf.op = this.tokenLocation($c, $r, 'currentRow' );
1990
+ $wf.op = this.valueWithTokenLocation( 'currentRow', $c, $r );
1833
1991
  $wf.args = [];
1834
1992
  }
1835
1993
  ;
1836
1994
 
1837
1995
  overClause returns [ over ]
1838
- @after { this.attachLocation($over); }
1996
+ @after { this.attachLocation( $over ); }
1839
1997
  :
1840
- o=OVER { $over = { op: this.tokenLocation( $o, null, 'over' ) , args: [] } }
1998
+ o=OVER { $over = { op: this.valueWithTokenLocation( 'over', $o ) , args: [] } }
1841
1999
  '('
1842
2000
  ( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
1843
2001
  ( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
@@ -1858,11 +2016,11 @@ limitClause[ inQuery ] returns [ query ]
1858
2016
  orderBySpec returns[ ob ]
1859
2017
  :
1860
2018
  e=expression { $ob = $e.expr; }
1861
- ( asc=ASC { $ob.sort = this.tokenLocation( $asc, undefined, 'asc' ); }
1862
- | desc=DESC { $ob.sort = this.tokenLocation( $desc, undefined, 'desc' ); }
2019
+ ( asc=ASC { $ob.sort = this.valueWithTokenLocation( 'asc', $asc ); }
2020
+ | desc=DESC { $ob.sort = this.valueWithTokenLocation( 'desc', $desc ); }
1863
2021
  )?
1864
2022
  ( nb=NULLS ne=( FIRST | LAST )
1865
- { $ob.nulls = this.tokenLocation( $nb, $ne, $ne.text.toLowerCase() ); }
2023
+ { $ob.nulls = this.valueWithTokenLocation( $ne.text.toLowerCase(), $nb, $ne ); }
1866
2024
  )?
1867
2025
  ;
1868
2026
 
@@ -1885,22 +2043,23 @@ queryPrimary returns[ query = {} ]
1885
2043
  { $query = this.surroundByParens( $qe.query, $open, $close ); }
1886
2044
  |
1887
2045
  select=SELECT
1888
- { $query = { op: this.tokenLocation( $select, undefined, 'SELECT' ), location: this.startLocation() }; }
2046
+ { $query = { op: this.valueWithTokenLocation( 'SELECT', $select ), location: this.startLocation() }; }
1889
2047
  (
1890
2048
  FROM querySource[ $query ]
1891
2049
  (
1892
- mixin=MIXIN '{'
2050
+ mixin=MIXIN '{' { $query.mixin = this.createDict(); }
1893
2051
  mixinElementDef[ $query ]*
1894
- '}' INTO
2052
+ '}' { this.setDictEndLocation( $query.mixin ); }
2053
+ INTO
1895
2054
  )?
1896
2055
  ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
1897
- { $query.quantifier = this.tokenLocation( $ad, undefined, $ad.text.toLowerCase() ); }
2056
+ { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
1898
2057
  )?
1899
2058
  bracedSelectItemListDef[ $query ]?
1900
2059
  excludingClause[ $query ]?
1901
2060
  |
1902
2061
  ( ad=( ALL | DISTINCT ) // TODO: or directly after SELECT ?
1903
- { $query.quantifier = this.tokenLocation( $ad, undefined, $ad.text.toLowerCase() ); }
2062
+ { $query.quantifier = this.valueWithTokenLocation( $ad.text.toLowerCase(), $ad ); }
1904
2063
  )?
1905
2064
  { $query.columns = []; } // set it early to avoid "wildcard" errors
1906
2065
  selectItemDef[ $query.columns ]
@@ -1954,8 +2113,8 @@ joinOp[ left ] returns[ table ] locals [ join ]
1954
2113
  | t1=RIGHT t2=OUTER? c=joinCardinality? op=JOIN { $join = 'right' }
1955
2114
  | t1=FULL t2=OUTER? c=joinCardinality? op=JOIN { $join = 'full' }
1956
2115
  )
1957
- { $table = { op: this.tokenLocation( $op, undefined, 'join' ),
1958
- join: this.tokenLocation( $t1 || $op, $t2, $join ),
2116
+ { $table = { op: this.valueWithTokenLocation( 'join', $op ),
2117
+ join: this.valueWithTokenLocation( $join, $t1 || $op, $t2 ),
1959
2118
  args: ($left ? [$left] : []),
1960
2119
  location: $left && $left.location };
1961
2120
  if ($ctx.c) $table.cardinality = $c.joinCard; }
@@ -2062,24 +2221,24 @@ fromPath[ qp, idkind ]
2062
2221
 
2063
2222
  condition returns [ cond ] locals [ args = [], orl = [] ]
2064
2223
  @after{
2065
- $cond = ($args.length == 1)
2224
+ $cond = ($args.length === 1)
2066
2225
  ? this.attachLocation( $args[0] )
2067
2226
  : this.attachLocation({ op: $orl[0], args: $args });
2068
2227
  }
2069
2228
  :
2070
2229
  c1=conditionAnd { $args.push($c1.cond); }
2071
- ( or=OR c2=conditionAnd { $args.push($c2.cond); $orl.push(this.tokenLocation( $or, undefined, 'or' ))} )*
2230
+ ( or=OR c2=conditionAnd { $args.push($c2.cond); $orl.push(this.valueWithTokenLocation( 'or', $or ))} )*
2072
2231
  ;
2073
2232
 
2074
2233
  conditionAnd returns [ cond ] locals [ args = [], andl = [] ]
2075
2234
  @after{
2076
- $cond = ($args.length == 1)
2235
+ $cond = ($args.length === 1)
2077
2236
  ? $args[0]
2078
2237
  : this.attachLocation({ op: $andl[0], args: $args });
2079
2238
  }
2080
2239
  :
2081
2240
  c1=conditionTerm { $args.push($c1.cond); }
2082
- ( and=AND c2=conditionTerm { $args.push($c2.cond); $andl.push(this.tokenLocation( $and, undefined, 'and' )) } )*
2241
+ ( and=AND c2=conditionTerm { $args.push($c2.cond); $andl.push(this.valueWithTokenLocation( 'and', $and )) } )*
2083
2242
  ;
2084
2243
 
2085
2244
  conditionTerm returns [ cond ]
@@ -2088,38 +2247,38 @@ conditionTerm returns [ cond ]
2088
2247
  }
2089
2248
  :
2090
2249
  nt=NOT ct=conditionTerm
2091
- { $cond = { op: this.tokenLocation( $nt, undefined, 'not' ), args: [ $ct.cond ] }; }
2250
+ { $cond = { op: this.valueWithTokenLocation( 'not', $nt ), args: [ $ct.cond ] }; }
2092
2251
  |
2093
2252
  ex=EXISTS
2094
2253
  (
2095
2254
  open='(' qe=queryExpression close=')'
2096
- { $cond = { op: this.tokenLocation( $ex, undefined, 'exists' ),
2255
+ { $cond = { op: this.valueWithTokenLocation( 'exists', $ex ),
2097
2256
  args: [ this.surroundByParens( $qe.query, $open, $close, true ) ] }; }
2098
2257
  |
2099
2258
  qm=( HideAlternatives | '?' )
2100
- { $cond = { op: this.tokenLocation( $ex, undefined, 'exists' ), args: [
2101
- { param: this.tokenLocation( $qm, undefined, '?' ), scope: 'param' }
2259
+ { $cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [
2260
+ { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' }
2102
2261
  ] };
2103
2262
  this.csnParseOnly( 'Dynamic parameter "?" is not supported', $qm );
2104
2263
  }
2105
2264
  |
2106
2265
  ep=valuePath[ 'ref' ]
2107
2266
  { $ep.qp['$'+'expected'] = 'exists';
2108
- $cond = { op: this.tokenLocation( $ex, undefined, 'exists' ), args: [ $ep.qp ] };
2267
+ $cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [ $ep.qp ] };
2109
2268
  }
2110
2269
  )
2111
2270
  |
2112
2271
  expr=expression // see @after
2113
2272
  (
2114
2273
  rel=( '=' | '<>' | '>' | '>=' | '<' | '<=' | '!=' )
2115
- { $cond = { op: this.tokenLocation( $rel, undefined, $rel.text), args: [ $expr.expr ] }; }
2274
+ { $cond = { op: this.valueWithTokenLocation( $rel.text, $rel ), args: [ $expr.expr ] }; }
2116
2275
  ( asa=( ANY | SOME | ALL )
2117
- { $cond.quantifier = this.tokenLocation($asa, undefined, $asa.text.toLowerCase()); }
2276
+ { $cond.quantifier = this.valueWithTokenLocation( $asa.text.toLowerCase(), $asa ); }
2118
2277
  )?
2119
2278
  e2=expression { $cond.args.push($e2.expr); }
2120
2279
  |
2121
2280
  IS ( inn=NOT NULL | innu=NULL )
2122
- { $cond = { op: $inn ? this.tokenLocation( $inn, undefined, 'isNotNull' ) : this.tokenLocation( $innu, undefined, 'isNull' ), args: [ $expr.expr ] }; }
2281
+ { $cond = { op: $inn ? this.valueWithTokenLocation( 'isNotNull', $inn ) : this.valueWithTokenLocation( 'isNull', $innu ), args: [ $expr.expr ] }; }
2123
2282
  |
2124
2283
  { $cond = { args: [ $expr.expr ] }; }
2125
2284
  NOT predicate[ $cond, true ]
@@ -2135,14 +2294,14 @@ predicate[ cond, negated ]
2135
2294
  // NOT (a BETWEEN b AND c)
2136
2295
  :
2137
2296
  ino=IN e1=expression // including ExpressionList
2138
- { $cond.op = this.tokenLocation( $ino, undefined, (negated) ? 'notIn' : 'in'); $cond.args.push( $e1.expr ); }
2297
+ { $cond.op = this.valueWithTokenLocation( (negated) ? 'notIn' : 'in', $ino ); $cond.args.push( $e1.expr ); }
2139
2298
  |
2140
2299
  bw=BETWEEN e2=expression
2141
- { $cond.op = this.tokenLocation( $bw, undefined, (negated) ? 'notBetween' : 'between' ); $cond.args.push( $e2.expr ); }
2300
+ { $cond.op = this.valueWithTokenLocation( (negated) ? 'notBetween' : 'between', $bw ); $cond.args.push( $e2.expr ); }
2142
2301
  AND e3=expression { $cond.args.push( $e3.expr ); }
2143
2302
  |
2144
2303
  lk=LIKE e4=expression
2145
- { $cond.op = this.tokenLocation( $lk, undefined, (negated) ? 'notLike' : 'like' ); $cond.args.push( $e4.expr ); }
2304
+ { $cond.op = this.valueWithTokenLocation( (negated) ? 'notLike' : 'like', $lk ); $cond.args.push( $e4.expr ); }
2146
2305
  ( ESCAPE e5=expression { $cond.args.push( $e5.expr ); } )?
2147
2306
  ;
2148
2307
 
@@ -2154,7 +2313,7 @@ expression returns [ expr ]
2154
2313
  or='||' e2=expressionSum
2155
2314
  {
2156
2315
  $expr = {
2157
- op: this.tokenLocation( $or, undefined, '||' ), args: [$expr, $e2.expr],
2316
+ op: this.valueWithTokenLocation( '||', $or ), args: [$expr, $e2.expr],
2158
2317
  location: this.combinedLocation( $expr, $e2.expr ) };
2159
2318
  }
2160
2319
  )*
@@ -2168,7 +2327,7 @@ expressionSum returns [ expr ]
2168
2327
  op=( '+' | '-' ) e2=expressionFactor
2169
2328
  {
2170
2329
  $expr = {
2171
- op: this.tokenLocation($op, undefined, $op.text), args: [$expr, $e2.expr],
2330
+ op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
2172
2331
  location: this.combinedLocation( $expr, $e2.expr ) };
2173
2332
  }
2174
2333
  )*
@@ -2182,7 +2341,7 @@ expressionFactor returns [ expr ]
2182
2341
  op=( '*' | '/' ) e2=expressionTerm
2183
2342
  {
2184
2343
  $expr = {
2185
- op: this.tokenLocation($op, undefined, $op.text), args: [$expr, $e2.expr],
2344
+ op: this.valueWithTokenLocation( $op.text, $op ), args: [$expr, $e2.expr],
2186
2345
  location: this.combinedLocation( $expr, $e2.expr ) };
2187
2346
  }
2188
2347
  )*
@@ -2202,7 +2361,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2202
2361
  { $expr = $sf.ret; }
2203
2362
  |
2204
2363
  ca=CASE
2205
- { $expr = { op : this.tokenLocation( $ca, undefined, 'case' ), args: [] }; }
2364
+ { $expr = { op : this.valueWithTokenLocation( 'case', $ca ), args: [] }; }
2206
2365
  (
2207
2366
  e2=expression { $expr.args.push($e2.expr); }
2208
2367
  ( ow=WHEN ew=expression THEN e3=expression
@@ -2220,7 +2379,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2220
2379
  |
2221
2380
  ne=NEW nqp=valuePath[ 'ref', null] // token rewrite for NEW
2222
2381
  // please note: there will be no compiler-supported code completion after NEW
2223
- { $expr = { op: this.tokenLocation( $ne, undefined, 'new' ), args: [] };
2382
+ { $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
2224
2383
  this.notSupportedYet( $ne ); }
2225
2384
  |
2226
2385
  vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
@@ -2239,7 +2398,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2239
2398
  )
2240
2399
  |
2241
2400
  qm=( HideAlternatives | '?' )
2242
- { $expr = { param: this.tokenLocation( $qm, undefined, '?' ), scope: 'param' };
2401
+ { $expr = { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' };
2243
2402
  this.csnParseOnly( 'Dynamic parameter "?" is not supported', $qm );
2244
2403
  }
2245
2404
  |
@@ -2257,7 +2416,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2257
2416
  close=')'
2258
2417
  {
2259
2418
  if ($expr.length > 1)
2260
- $expr = { op: this.tokenLocation( $open, undefined, ',' ), args: $expr };
2419
+ $expr = { op: this.valueWithTokenLocation( ',', $open ), args: $expr };
2261
2420
  else if ($expr[0]) // can be `null` if condition failed to parse
2262
2421
  $expr = this.surroundByParens( $expr[0], $open, $close );
2263
2422
  }
@@ -2297,7 +2456,7 @@ specialFunction returns [ ret = { } ] locals[ art = {} ]
2297
2456
  ca=CAST open='('
2298
2457
  {
2299
2458
  $ret = {
2300
- op: this.tokenLocation( $ca, undefined, 'cast' ),
2459
+ op: this.valueWithTokenLocation( 'cast', $ca ),
2301
2460
  args: [ ],
2302
2461
  location: this.tokenLocation( $ca )
2303
2462
  };
@@ -2345,6 +2504,7 @@ fromArguments[ pathStep ]
2345
2504
  pathArguments[ pathStep, considerSpecial ]
2346
2505
  @after{ /* #ATN 1 */ }
2347
2506
  :
2507
+ { this.excludeExpected([ 'ORDER' ]); }
2348
2508
  paren='('
2349
2509
  { this.prepareGenericKeywords( $considerSpecial ); }
2350
2510
  // ATN, LL2: Identifier can start both named arguments and the positional.
@@ -2366,16 +2526,23 @@ pathArguments[ pathStep, considerSpecial ]
2366
2526
  ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
2367
2527
  funcExpression[ $pathStep, $considerSpecial ]
2368
2528
  )*
2529
+ // Note: We can't move this into funcExpression, or we would increase the ATN count because of `,` amiguity.
2530
+ ( ob=functionOrderByClause[ $pathStep.args[$pathStep.args.length - 1] ]
2531
+ {
2532
+ // Remove the last entry which was copied to $ob.expr and push $ob.expr.
2533
+ $pathStep.args[$pathStep.args.length - 1] = $ob.expr;
2534
+ }
2535
+ )?
2369
2536
  |
2370
- a=ALL { $pathStep.quantifier = this.tokenLocation( $a, undefined, 'all' ); }
2537
+ a=ALL { $pathStep.quantifier = this.valueWithTokenLocation( 'all', $a ); }
2371
2538
  e1=expression { $pathStep.args = [ $e1.expr ]; }
2372
2539
  |
2373
- d=DISTINCT { $pathStep.quantifier = this.tokenLocation( $d, undefined, 'distinct' ); }
2540
+ d=DISTINCT { $pathStep.quantifier = this.valueWithTokenLocation( 'distinct', $d ); }
2374
2541
  e1=expression { $pathStep.args = [ $e1.expr ]; }
2375
2542
  ( ',' e2=expression { $pathStep.args.push( $e2.expr ); } )*
2376
2543
  |
2377
2544
  star='*'
2378
- { $pathStep.args = [ { location: this.tokenLocation($star), val: '*', literal: 'token' } ]; }
2545
+ { $pathStep.args = [ { location: this.tokenLocation( $star ), val: '*', literal: 'token' } ]; }
2379
2546
  |
2380
2547
  { $pathStep.args = []; }
2381
2548
  )
@@ -2449,7 +2616,7 @@ optionalWhereForFilter
2449
2616
 
2450
2617
  // Simple paths and values ---------------------------------------------------
2451
2618
 
2452
- annoValueBase returns[ val ] locals [ hasEllipsis=0 ]
2619
+ annoValueBase returns[ val ] locals [ seenEllipsis = false ]
2453
2620
  @after { this.attachLocation($val); }
2454
2621
  :
2455
2622
  { $val = { literal: 'struct', location: this.startLocation() }; }
@@ -2469,30 +2636,43 @@ annoValueBase returns[ val ] locals [ hasEllipsis=0 ]
2469
2636
  '['
2470
2637
  (
2471
2638
  (
2472
- head=arrayValue { $val.val.push( $head.val ); }
2473
- |
2474
- e='...'
2475
- {
2476
- $val.val.push( { literal: 'token', val: '...', location: this.tokenLocation($e) } );
2477
- $hasEllipsis++;
2478
- }
2639
+ head=arrayValue { $val.val.push( $head.val ); }
2640
+ |
2641
+ e='...' ( UP TO upTo=arrayValue )?
2642
+ {{
2643
+ const item = { literal: 'token', val: '...', location: this.tokenLocation($e) };
2644
+ $val.val.push( item );
2645
+ if ($ctx.upTo) item.upTo = $upTo.val;
2646
+ $seenEllipsis = !$ctx.upTo || 'upTo';
2647
+ }}
2479
2648
  )
2480
2649
  (
2481
2650
  ',' { if (this.isStraightBefore(']')) break; } // allow ',' before ']'
2482
2651
  (
2483
- tail=arrayValue { $val.val.push( $tail.val ); }
2484
- |
2485
- e='...'
2486
- {
2487
- $val.val.push( { literal: 'token', val: '...', location: this.tokenLocation($e) } );
2488
- if(++$hasEllipsis > 1)
2489
- this.error( 'syntax-unexpected-ellipsis', $e, { code: '...' },
2490
- 'Expected no more than one $(CODE)' );
2491
- }
2652
+ tail=arrayValue { $val.val.push( $tail.val ); }
2653
+ |
2654
+ { $ctx.upTo = null; } // is not reset
2655
+ e='...' ( UP TO upTo=arrayValue )?
2656
+ {{
2657
+ const item = { literal: 'token', val: '...', location: this.tokenLocation($e) };
2658
+ if ($ctx.upTo) item.upTo = $upTo.val;
2659
+ $val.val.push( item );
2660
+ if ($seenEllipsis === true) // TODO: adapt msg to UP TO
2661
+ this.error( 'syntax-unexpected-ellipsis', $e, { code: '...' },
2662
+ 'Expected no more than one $(CODE)' );
2663
+ else
2664
+ $seenEllipsis = !$ctx.upTo || 'upTo';
2665
+ }}
2492
2666
  )
2493
2667
  )*
2494
2668
  )?
2495
- ']'
2669
+ cb=']'
2670
+ {
2671
+ if ($seenEllipsis === 'upTo')
2672
+ this.error( 'syntax-expecting-ellipsis', $cb, // at closing bracket
2673
+ { code: '... up to', newcode: '...' },
2674
+ 'Expecting an array item $(NEWCODE) after an item with $(CODE)' );
2675
+ }
2496
2676
  |
2497
2677
  v1=literalValue { $val = $v1.val; }
2498
2678
  |
@@ -2660,6 +2840,7 @@ ident[ category ] returns[ id ]
2660
2840
  | ASSOCIATION
2661
2841
  | BETWEEN
2662
2842
  | BOTH
2843
+ | COLUMNS
2663
2844
  | COMPOSITION
2664
2845
  | CONTEXT
2665
2846
  | CROSS
@@ -2667,8 +2848,10 @@ ident[ category ] returns[ id ]
2667
2848
  | DAY
2668
2849
  | DEFAULT
2669
2850
  | DEFINE
2851
+ | DEFINITIONS
2670
2852
  | DESC
2671
2853
  | ELEMENT
2854
+ | ELEMENTS
2672
2855
  | ELSE
2673
2856
  | END
2674
2857
  | ENTITY
@@ -2725,6 +2908,7 @@ ident[ category ] returns[ id ]
2725
2908
  | THEN
2726
2909
  | TRAILING
2727
2910
  | UNION
2911
+ | UP
2728
2912
  | TO
2729
2913
  | TYPE
2730
2914
  | USING
@@ -2749,9 +2933,40 @@ LineComment : '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN);
2749
2933
 
2750
2934
  // Values --------------------------------------------------------------------
2751
2935
 
2752
- String
2936
+ // for syntactic code-completion: Combine all three string styles
2937
+ // Note: Use rule `string` instead as that also parses escape sequences!
2938
+ String : SingleLineString
2939
+ | MultiLineString
2940
+ | MutlLineStringBlock;
2941
+
2942
+ fragment SingleLineString
2753
2943
  :
2754
- ( '\'' ~[\u0027\n\r\u2028\u2029]* '\'' )+ // \u0027 = '\''
2944
+ // \u0027 = '\''
2945
+ // \u2028 = LS (Line Separator)
2946
+ // \u2029 = PS (Paragraph Separator)
2947
+ ( '\'' ~[\u0027\n\r\u2028\u2029]* '\'' )+ //
2948
+ ;
2949
+
2950
+ fragment MultiLineString
2951
+ :
2952
+ ('`' ( MultiLineStringContentChar | EscapeSequence )* '`' )
2953
+ ;
2954
+
2955
+ fragment MutlLineStringBlock
2956
+ :
2957
+ ('```' ( MultiLineStringContentChar | EscapeSequence )* '```')
2958
+ ;
2959
+
2960
+ fragment EscapeSequence
2961
+ :
2962
+ // we could list each escape sequence explicitly, but we already
2963
+ // decode them in genericAntlrParser.js, so no need to do work twice.
2964
+ '\\' .
2965
+ ;
2966
+
2967
+ fragment MultiLineStringContentChar
2968
+ :
2969
+ (~[\u0060\\]) // \u0060 = '`'
2755
2970
  ;
2756
2971
 
2757
2972
  QuotedLiteral
@@ -2760,10 +2975,15 @@ QuotedLiteral
2760
2975
  ( '\'' ~[\u0027\n\r\u2028\u2029]* '\'' )+ // \u0027 = '\''
2761
2976
  ;
2762
2977
 
2978
+ // This literal improves error messages for unterminated literals.
2763
2979
  UnterminatedLiteral
2764
2980
  :
2765
2981
  ( [xX] | [dD][aA][tT][eE] | [tT][iI][mM][eE] ( [sS][tT][aA][mM][pP] )? )?
2766
2982
  '\'' ~[\u0027\n\r\u2028\u2029]* // \u0027 = '\''
2983
+ |
2984
+ ('`' ( MultiLineStringContentChar | EscapeSequence )* )
2985
+ |
2986
+ ('```' ( MultiLineStringContentChar | EscapeSequence )* )
2767
2987
  ;
2768
2988
 
2769
2989
  UnterminatedDelimitedIdentifier
@@ -2827,6 +3047,7 @@ ASPECT : [aA][sS][pP][eE][cC][tT] ;
2827
3047
  ASSOCIATION : [aA][sS][sS][oO][cC][iI][aA][tT][iI][oO][nN] ;
2828
3048
  BETWEEN : [bB][eE][tT][wW][eE][eE][nN] ;
2829
3049
  BOTH : [bB][oO][tT][hH] ;
3050
+ COLUMNS : [cC][oO][lL][uU][mM][nN][sS];
2830
3051
  COMPOSITION : [cC][oO][mM][pP][oO][sS][iI][tT][iI][oO][nN] ;
2831
3052
  CONTEXT : [cC][oO][nN][tT][eE][xX][tT] ;
2832
3053
  CROSS : [cC][rR][oO][sS][sS] ;
@@ -2834,8 +3055,10 @@ CURRENT : [cC][uU][rR][rR][eE][nN][tT] ;
2834
3055
  DAY : [dD][aA][yY] ;
2835
3056
  DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
2836
3057
  DEFINE : [dD][eE][fF][iI][nN][eE] ;
3058
+ DEFINITIONS : [dD][eE][fF][iI][nN][iI][tT][iI][oO][nN][sS] ;
2837
3059
  DESC : [dD][eE][sS][cC] ;
2838
3060
  ELEMENT : [eE][lL][eE][mM][eE][nN][tT] ;
3061
+ ELEMENTS : [eE][lL][eE][mM][eE][nN][tT][sS] ;
2839
3062
  ELSE : [eE][lL][sS][eE] ;
2840
3063
  END : [eE][nN][dD] ;
2841
3064
  ENTITY : [eE][nN][tT][iI][tT][yY] ;
@@ -2896,6 +3119,7 @@ TO : [tT][oO] ; // or make reserved? (is in SQL-92)
2896
3119
  TYPE : [tT][yY][pP][eE] ;
2897
3120
  UNION : [uU][nN][iI][oO][nN] ;
2898
3121
  UNBOUNDED : [uU][nN][bB][oO][uU][nN][dD][eE][dD] ;
3122
+ UP : [uU][pP] ;
2899
3123
  USING : [uU][sS][iI][nN][gG] ;
2900
3124
  VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
2901
3125
  VIEW : [vV][iI][eE][wW] ;