@@ -96,8 +96,12 @@ object JavaParsers {
96
96
def javaLangDot (name : Name ): Tree =
97
97
Select (javaDot(nme.lang), name)
98
98
99
+ /** Tree representing `java.lang.Object` */
99
100
def javaLangObject (): Tree = javaLangDot(tpnme.Object )
100
101
102
+ /** Tree representing `java.lang.Record` */
103
+ def javaLangRecord (): Tree = javaLangDot(tpnme.Record )
104
+
101
105
def arrayOf (tpt : Tree ): AppliedTypeTree =
102
106
AppliedTypeTree (scalaDot(tpnme.Array ), List (tpt))
103
107
@@ -555,6 +559,14 @@ object JavaParsers {
555
559
556
560
def definesInterface (token : Int ): Boolean = token == INTERFACE || token == AT
557
561
562
+ /** If the next token is the identifier "record", convert it into the RECORD token.
563
+ * This makes it easier to handle records in various parts of the code,
564
+ * in particular when a `parentToken` is passed to some functions.
565
+ */
566
+ def adaptRecordIdentifier (): Unit =
567
+ if in.token == IDENTIFIER && in.name == jnme.RECORDid then
568
+ in.token = RECORD
569
+
558
570
def termDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = {
559
571
val inInterface = definesInterface(parentToken)
560
572
val tparams = if (in.token == LT ) typeParams(Flags .JavaDefined | Flags .Param ) else List ()
@@ -581,6 +593,16 @@ object JavaParsers {
581
593
TypeTree (), methodBody()).withMods(mods)
582
594
}
583
595
}
596
+ } else if (in.token == LBRACE && rtptName != nme.EMPTY && parentToken == RECORD ) {
597
+ /*
598
+ record RecordName(T param1, ...) {
599
+ RecordName { // <- here
600
+ // methodBody
601
+ }
602
+ }
603
+ */
604
+ methodBody()
605
+ Nil
584
606
}
585
607
else {
586
608
var mods1 = mods
@@ -717,12 +739,11 @@ object JavaParsers {
717
739
ValDef (name, tpt2, if (mods.is(Flags .Param )) EmptyTree else unimplementedExpr).withMods(mods1)
718
740
}
719
741
720
- def memberDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = in.token match {
721
- case CLASS | ENUM | INTERFACE | AT =>
722
- typeDecl(start, if ( definesInterface(parentToken)) mods | Flags .JavaStatic else mods)
742
+ def memberDecl (start : Offset , mods : Modifiers , parentToken : Int , parentTParams : List [TypeDef ]): List [Tree ] = in.token match
743
+ case CLASS | ENUM | RECORD | INTERFACE | AT =>
744
+ typeDecl(start, if definesInterface(parentToken) then mods | Flags .JavaStatic else mods)
723
745
case _ =>
724
746
termDecl(start, mods, parentToken, parentTParams)
725
- }
726
747
727
748
def makeCompanionObject (cdef : TypeDef , statics : List [Tree ]): Tree =
728
749
atSpan(cdef.span) {
@@ -804,6 +825,48 @@ object JavaParsers {
804
825
addCompanionObject(statics, cls)
805
826
}
806
827
828
+ def recordDecl (start : Offset , mods : Modifiers ): List [Tree ] =
829
+ accept(RECORD )
830
+ val nameOffset = in.offset
831
+ val name = identForType()
832
+ val tparams = typeParams()
833
+ val header = formalParams()
834
+ val superclass = javaLangRecord() // records always extend java.lang.Record
835
+ val interfaces = interfacesOpt() // records may implement interfaces
836
+ val (statics, body) = typeBody(RECORD , name, tparams)
837
+
838
+ // We need to generate accessors for every param, if no method with the same name is already defined
839
+
840
+ var fieldsByName = header.map(v => (v.name, (v.tpt, v.mods.annotations))).toMap
841
+
842
+ for case DefDef (name, paramss, tpt, rhs) <- body
843
+ if paramss.isEmpty && fieldsByName.contains(name)
844
+ do
845
+ fieldsByName -= name
846
+ end for
847
+
848
+ val accessors =
849
+ (for (name, (tpt, annots)) <- fieldsByName yield
850
+ DefDef (name, Nil , tpt, unimplementedExpr).withMods(Modifiers (Flags .JavaDefined | Flags .Method | Flags .Synthetic ))
851
+ ).toList
852
+
853
+ // generate the canonical constructor
854
+ val canonicalConstructor = makeConstructor(header, tparams)
855
+
856
+ // return the trees, probably with addCompanionObject (like classDecl)
857
+ val recordTypeDef = atSpan(start, nameOffset) {
858
+ TypeDef (name,
859
+ makeTemplate(
860
+ parents = superclass :: interfaces,
861
+ stats = canonicalConstructor :: accessors ::: body,
862
+ tparams = tparams,
863
+ false
864
+ )
865
+ )
866
+ }
867
+ addCompanionObject(statics, recordTypeDef)
868
+ end recordDecl
869
+
807
870
def interfaceDecl (start : Offset , mods : Modifiers ): List [Tree ] = {
808
871
accept(INTERFACE )
809
872
val nameOffset = in.offset
@@ -846,7 +909,8 @@ object JavaParsers {
846
909
else if (in.token == SEMI )
847
910
in.nextToken()
848
911
else {
849
- if (in.token == ENUM || definesInterface(in.token)) mods |= Flags .JavaStatic
912
+ adaptRecordIdentifier()
913
+ if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) mods |= Flags .JavaStatic
850
914
val decls = memberDecl(start, mods, parentToken, parentTParams)
851
915
(if (mods.is(Flags .JavaStatic ) || inInterface && ! (decls exists (_.isInstanceOf [DefDef ])))
852
916
statics
@@ -947,13 +1011,13 @@ object JavaParsers {
947
1011
}
948
1012
}
949
1013
950
- def typeDecl (start : Offset , mods : Modifiers ): List [Tree ] = in.token match {
1014
+ def typeDecl (start : Offset , mods : Modifiers ): List [Tree ] = in.token match
951
1015
case ENUM => enumDecl(start, mods)
952
1016
case INTERFACE => interfaceDecl(start, mods)
953
1017
case AT => annotationDecl(start, mods)
954
1018
case CLASS => classDecl(start, mods)
1019
+ case RECORD => recordDecl(start, mods)
955
1020
case _ => in.nextToken(); syntaxError(em " illegal start of type declaration " , skipIt = true ); List (errorTypeTree)
956
- }
957
1021
958
1022
def tryConstant : Option [Constant ] = {
959
1023
val negate = in.token match {
@@ -1004,6 +1068,7 @@ object JavaParsers {
1004
1068
if (in.token != EOF ) {
1005
1069
val start = in.offset
1006
1070
val mods = modifiers(inInterface = false )
1071
+ adaptRecordIdentifier() // needed for typeDecl
1007
1072
buf ++= typeDecl(start, mods)
1008
1073
}
1009
1074
}
0 commit comments