Skip to content

Commit

Permalink
Support records in JavaParsers
Browse files Browse the repository at this point in the history
This is a port of scala/scala#9551
  • Loading branch information
TheElectronWill committed Mar 5, 2023
1 parent ce65296 commit 6ec3616
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 4 deletions.
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Expand Up @@ -204,6 +204,7 @@ object StdNames {
final val Null: N = "Null"
final val Object: N = "Object"
final val FromJavaObject: N = "<FromJavaObject>"
final val Record: N = "Record"
final val Product: N = "Product"
final val PartialFunction: N = "PartialFunction"
final val PrefixType: N = "PrefixType"
Expand Down Expand Up @@ -912,6 +913,10 @@ object StdNames {
final val VOLATILEkw: N = kw("volatile")
final val WHILEkw: N = kw("while")

final val RECORDid: N = "record"
final val VARid: N = "var"
final val YIELDid: N = "yield"

final val BoxedBoolean: N = "java.lang.Boolean"
final val BoxedByte: N = "java.lang.Byte"
final val BoxedCharacter: N = "java.lang.Character"
Expand Down Expand Up @@ -944,6 +949,8 @@ object StdNames {
final val JavaSerializable: N = "java.io.Serializable"
}



class JavaTermNames extends JavaNames[TermName] {
protected def fromString(s: String): TermName = termName(s)
}
Expand Down
66 changes: 62 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Expand Up @@ -98,6 +98,8 @@ object JavaParsers {

def javaLangObject(): Tree = javaLangDot(tpnme.Object)

def javaLangRecord(): Tree = javaLangDot(tpnme.Record)

def arrayOf(tpt: Tree): AppliedTypeTree =
AppliedTypeTree(scalaDot(tpnme.Array), List(tpt))

Expand Down Expand Up @@ -555,6 +557,10 @@ object JavaParsers {

def definesInterface(token: Int): Boolean = token == INTERFACE || token == AT

def adaptRecordIdentifier(): Unit =
if in.token == IDENTIFIER && in.name == jnme.RECORDid then
in.token = RECORD

def termDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = {
val inInterface = definesInterface(parentToken)
val tparams = if (in.token == LT) typeParams(Flags.JavaDefined | Flags.Param) else List()
Expand All @@ -581,6 +587,16 @@ object JavaParsers {
TypeTree(), methodBody()).withMods(mods)
}
}
} else if (in.token == LBRACE && rtptName != nme.EMPTY && parentToken == RECORD) {
/*
record RecordName(T param1, ...) {
RecordName { // <- here
// methodBody
}
}
*/
methodBody()
Nil
}
else {
var mods1 = mods
Expand Down Expand Up @@ -717,12 +733,11 @@ object JavaParsers {
ValDef(name, tpt2, if (mods.is(Flags.Param)) EmptyTree else unimplementedExpr).withMods(mods1)
}

def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match {
case CLASS | ENUM | INTERFACE | AT =>
def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match
case CLASS | ENUM | RECORD | INTERFACE | AT =>
typeDecl(start, if (definesInterface(parentToken)) mods | Flags.JavaStatic else mods)
case _ =>
termDecl(start, mods, parentToken, parentTParams)
}

def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree =
atSpan(cdef.span) {
Expand Down Expand Up @@ -804,6 +819,48 @@ object JavaParsers {
addCompanionObject(statics, cls)
}

def recordDecl(start: Offset, mods: Modifiers): List[Tree] =
accept(RECORD)
val nameOffset = in.offset
val name = identForType()
val tparams = typeParams()
val header = formalParams()
val superclass = javaLangRecord() // records always extend java.lang.Record
val interfaces = interfacesOpt() // records may implement interfaces
val (statics, body) = typeBody(RECORD, name, tparams)

// We need to generate accessors for every param, if no method with the same name is already defined

var fieldsByName = header.map(v => (v.name, (v.tpt, v.mods.annotations))).toMap

for case DefDef(name, paramss, tpt, rhs) <- body
if paramss.isEmpty && fieldsByName.contains(name)
do
fieldsByName -= name
end for

val accessors =
(for (name, (tpt, annots)) <- fieldsByName yield
DefDef(name, Nil, tpt, unimplementedExpr).withMods(Modifiers(Flags.JavaDefined | Flags.Method | Flags.Synthetic))
).toList

// generate the canonical constructor
val canonicalConstructor = makeConstructor(header, tparams)

// return the trees, probably with addCompanionObject (like classDecl)
val recordTypeDef = atSpan(start, nameOffset) {
TypeDef(name,
makeTemplate(
parents = superclass :: interfaces,
stats = canonicalConstructor :: accessors ::: body,
tparams = tparams,
false
)
)
}
addCompanionObject(statics, recordTypeDef)
end recordDecl

def interfaceDecl(start: Offset, mods: Modifiers): List[Tree] = {
accept(INTERFACE)
val nameOffset = in.offset
Expand Down Expand Up @@ -846,7 +903,7 @@ object JavaParsers {
else if (in.token == SEMI)
in.nextToken()
else {
if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.JavaStatic
if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) mods |= Flags.JavaStatic
val decls = memberDecl(start, mods, parentToken, parentTParams)
(if (mods.is(Flags.JavaStatic) || inInterface && !(decls exists (_.isInstanceOf[DefDef])))
statics
Expand Down Expand Up @@ -952,6 +1009,7 @@ object JavaParsers {
case INTERFACE => interfaceDecl(start, mods)
case AT => annotationDecl(start, mods)
case CLASS => classDecl(start, mods)
case RECORD => recordDecl(start, mods)
case _ => in.nextToken(); syntaxError(em"illegal start of type declaration", skipIt = true); List(errorTypeTree)
}

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala
Expand Up @@ -22,6 +22,7 @@ object JavaTokens extends TokensCommon {
inline val INTERFACE = 105; enter(INTERFACE, "interface")
inline val ENUM = 106; enter(ENUM, "enum")
inline val IMPLEMENTS = 107; enter(IMPLEMENTS, "implements")
inline val RECORD = 108; enter(RECORD, "record")

/** modifiers */
inline val PUBLIC = 110; enter(PUBLIC, "public")
Expand Down

0 comments on commit 6ec3616

Please sign in to comment.