From 41d83d42650d0c0b54c47c1a9043d0b92315aa4e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 8 Feb 2017 21:19:16 +1100 Subject: Change handling of enum defs. The previous scheme did not work because desugaring cannot deal with repeated expansions. We now sidestep the issue by doing the expansion in the parser. Luckily, positions work out perfectly, so that one can reconstruct the source precisely from the parsed untyped trees. --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 41 +++++++++++++++------- 1 file changed, 29 insertions(+), 12 deletions(-) (limited to 'compiler/src/dotty/tools/dotc/parsing/Parsers.scala') diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7ec44e342..ee736179a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -49,6 +49,13 @@ object Parsers { val Class, Type, TypeParam, Def = Value } + private implicit class AddDeco(val buf: ListBuffer[Tree]) extends AnyVal { + def +++=(x: Tree) = x match { + case x: Thicket => buf ++= x.trees + case x => buf += x + } + } + /** The parse starting point depends on whether the source file is self-contained: * if not, the AST will be supplemented. */ @@ -2061,8 +2068,9 @@ object Parsers { case CASEOBJECT => objectDef(start, posMods(start, mods | Case | Module)) case ENUM => - val mods1 = addMod(mods, atPos(in.skipToken()) { Mod.Enum() }) - if (in.token == CLASS) tmplDef(start, mods1) else enumDef(start, mods) + val enumMod = atPos(in.skipToken()) { Mod.Enum() } + if (in.token == CLASS) tmplDef(start, addMod(mods, enumMod)) + else enumDef(start, mods, enumMod) case _ => syntaxErrorOrIncomplete("expected start of definition") EmptyTree @@ -2115,9 +2123,11 @@ object Parsers { /** id ClassConstr [`extends' [ConstrApps]] * [nl] ‘{’ EnumCaseStats ‘}’ */ - def enumDef(start: Offset, mods: Modifiers): EnumDef = atPos(start, nameStart) { - val name = ident().toTypeName - val constr = classConstr(name) + def enumDef(start: Offset, mods: Modifiers, enumMod: Mod): Thicket = { + val point = nameStart + val modName = ident() + val clsName = modName.toTypeName + val constr = classConstr(clsName) val parents = if (in.token == EXTENDS) { in.nextToken(); @@ -2125,10 +2135,17 @@ object Parsers { if (in.token == LBRACE) Nil else tokenSeparated(WITH, constrApp) } else Nil + val clsDef = atPos(start, point) { + TypeDef(clsName, Template(constr, parents, EmptyValDef, Nil)) + .withMods(addMod(mods, enumMod)).setComment(in.getDocComment(start)) + } newLineOptWhenFollowedBy(LBRACE) - val body = inBraces(enumCaseStats) - EnumDef(name, Template(constr, Nil, EmptyValDef, body)) - .withMods(mods).setComment(in.getDocComment(start)).asInstanceOf[EnumDef] + val modDef = atPos(in.offset) { + val body = inBraces(enumCaseStats) + ModuleDef(modName, Template(emptyConstructor, Nil, EmptyValDef, body)) + .withMods(mods) + } + Thicket(clsDef :: modDef :: Nil) } /** EnumCaseStats = EnumCaseStat {semi EnumCaseStat */ @@ -2249,7 +2266,7 @@ object Parsers { else if (in.token == IMPORT) stats ++= importClause() else if (in.token == AT || isTemplateIntro || isModifier) - stats += tmplDef(in.offset, defAnnotsMods(modifierTokens)) + stats +++= tmplDef(in.offset, defAnnotsMods(modifierTokens)) else if (!isStatSep) { if (in.token == CASE) syntaxErrorOrIncomplete("only `case class` or `case object` allowed") @@ -2299,7 +2316,7 @@ object Parsers { else if (isExprIntro) stats += expr1() else if (isDefIntro(modifierTokensOrCase)) - stats += defOrDcl(in.offset, defAnnotsMods(modifierTokens)) + stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) else if (!isStatSep) { exitOnError = mustStartStat syntaxErrorOrIncomplete("illegal start of definition") @@ -2357,9 +2374,9 @@ object Parsers { val start = in.offset val imods = implicitMods() if (isBindingIntro) stats += implicitClosure(start, Location.InBlock, imods) - else stats += localDef(start, imods) + else stats +++= localDef(start, imods) } else { - stats += localDef(in.offset) + stats +++= localDef(in.offset) } else if (!isStatSep && (in.token != CASE)) { exitOnError = mustStartStat -- cgit v1.2.3