Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change to given/using syntax #8162

Merged
merged 8 commits into from Feb 2, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Expand Up @@ -601,6 +601,7 @@ object StdNames {
val universe: N = "universe"
val update: N = "update"
val updateDynamic: N = "updateDynamic"
val using: N = "using"
val value: N = "value"
val valueOf : N = "valueOf"
val values: N = "values"
Expand Down
114 changes: 36 additions & 78 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Expand Up @@ -911,11 +911,14 @@ object Parsers {

/** Are the next tokens a prefix of a formal parameter or given type?
* @pre: current token is LPAREN
* TODO: Drop once syntax has stabilized
*/
def followingIsParamOrGivenType() =
val lookahead = in.LookaheadScanner()
lookahead.nextToken()
if startParamOrGivenTypeTokens.contains(lookahead.token) then true
if startParamOrGivenTypeTokens.contains(lookahead.token)
|| lookahead.isIdent(nme.using)
then true
else if lookahead.token == IDENTIFIER then
if lookahead.name == nme.inline then
lookahead.nextToken()
Expand Down Expand Up @@ -944,7 +947,6 @@ object Parsers {
else
lookahead.token == SUBTYPE // TODO: remove
|| lookahead.isIdent(nme.as)
|| lookahead.token == WITH && lookahead.ch != Chars.LF // TODO: remove LF test

def followingIsExtension() =
val lookahead = in.LookaheadScanner()
Expand Down Expand Up @@ -1344,8 +1346,8 @@ object Parsers {
* MonoFunType ::= FunArgTypes (‘=>’ | ‘?=>’) Type
* PolyFunType ::= HKTypeParamClause '=>' Type
* FunArgTypes ::= InfixType
* | `(' [ [ ‘['erased'] FunArgType {`,' FunArgType } ] `)'
* | '(' [ ‘['erased'] TypedFunParam {',' TypedFunParam } ')'
* | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)'
* | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')'
*/
def typ(): Tree = {
val start = in.offset
Expand Down Expand Up @@ -2184,7 +2186,6 @@ object Parsers {
* | SimpleExpr `.' MatchClause
* | SimpleExpr (TypeArgs | NamedTypeArgs)
* | SimpleExpr1 ArgumentExprs
* | SimpleExpr ContextArguments
* Quoted ::= ‘'’ ‘{’ Block ‘}’
* | ‘'’ ‘[’ Type ‘]’
*/
Expand Down Expand Up @@ -2253,8 +2254,6 @@ object Parsers {
case DOT =>
in.nextToken()
simpleExprRest(selector(t), canApply = true)
case DOTWITH =>
simpleExprRest(contextArguments(t), canApply = true)
case LBRACKET =>
val tapp = atSpan(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true, wildOK = false)) }
simpleExprRest(tapp, canApply = true)
Expand Down Expand Up @@ -2294,13 +2293,13 @@ object Parsers {
def exprsInParensOpt(): List[Tree] =
if (in.token == RPAREN) Nil else commaSeparated(exprInParens)

/** ParArgumentExprs ::= `(' [‘given’] [ExprsInParens] `)'
/** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)'
* | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')'
*/
def parArgumentExprs(): (List[Tree], Boolean) = inParens {
if in.token == RPAREN then
(Nil, false)
else if in.token == GIVEN then
else if in.token == GIVEN || isIdent(nme.using) then
in.nextToken()
(commaSeparated(argumentExpr), true)
else
Expand Down Expand Up @@ -2331,7 +2330,7 @@ object Parsers {
else fn
}

/** ParArgumentExprss ::= {ParArgumentExprs | ContextArguments}
/** ParArgumentExprss ::= {ParArgumentExprs}
*
* Special treatment for arguments to primary constructor annotations.
* (...) is considered an argument only if it does not look like a formal
Expand All @@ -2356,8 +2355,6 @@ object Parsers {
parArgumentExprss(
atSpan(startOffset(fn)) { mkApply(fn, parArgumentExprs()) }
)
else if in.token == DOTWITH then
parArgumentExprss(contextArguments(fn))
else fn
}

Expand Down Expand Up @@ -2387,14 +2384,6 @@ object Parsers {
else Block(stats, EmptyTree)
}

/** ContextArguments ::= ‘.’ ‘with’ ArgumentExprs */
def contextArguments(t: Tree): Tree =
if in.token == DOTWITH then
atSpan(t.span.start, in.skipToken()) {
Apply(t, argumentExprs()._1).setGivenApply()
}
else t

/** Guard ::= if PostfixExpr
*/
def guard(): Tree =
Expand Down Expand Up @@ -2886,23 +2875,22 @@ object Parsers {
def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] =
if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil

/** AnnotTypes ::= AnnotType {‘,’ AnnotType}
* Types ::= Type {‘,’ Type}
/** ContextTypes ::= Type {‘,’ Type}
*/
def givenTypes(parseType: () => Tree, nparams: Int, ofClass: Boolean): List[ValDef] =
val tps = commaSeparated(parseType)
def contextTypes(ofClass: Boolean, nparams: Int): List[ValDef] =
val tps = commaSeparated(typ)
var counter = nparams
def nextIdx = { counter += 1; counter }
val paramFlags = if ofClass then Private | Local | ParamAccessor else Param
tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given))

/** ClsParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’
* GivenClsParamClause::= 'with' (‘(’ (ClsParams | Types) ‘)’ | AnnotTypes)
/** ClsParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’ | UsingClsParamClause
* UsingClsParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’
* ClsParams ::= ClsParam {‘,’ ClsParam}
* ClsParam ::= {Annotation}
*
* DefParamClause ::= ‘(’ [‘erased’] DefParams ‘)’
* GivenParamClause ::= ‘with’ (‘(’ (DefParams | Types) ‘)’ | AnnotTypes)
* DefParamClause ::= ‘(’ [‘erased’] DefParams ‘)’ | UsingParamClause
* UsingParamClause ::= ‘(’ ‘using’ [‘erased’] (DefParams | ContextTypes) ‘)’
* DefParams ::= DefParam {‘,’ DefParam}
* DefParam ::= {Annotation} [‘inline’] Param
*
Expand All @@ -2915,17 +2903,17 @@ object Parsers {
ofCaseClass: Boolean = false, // owner is a case class
prefix: Boolean = false, // clause precedes name of an extension method
givenOnly: Boolean = false, // only given parameters allowed
firstClause: Boolean = false, // clause is the first in regular list of clauses
prefixMods: Modifiers = EmptyModifiers // is `Given` if this is a with clause
firstClause: Boolean = false // clause is the first in regular list of clauses
): List[ValDef] = {
var impliedMods: Modifiers = prefixMods
var impliedMods: Modifiers = EmptyModifiers

def impliedModOpt(token: Token, mod: () => Mod): Boolean =
if in.token == token then
impliedMods = addMod(impliedMods, atSpan(in.skipToken()) { mod() })
true
def addParamMod(mod: () => Mod) = impliedMods = addMod(impliedMods, atSpan(in.skipToken()) { mod() })

def paramMods() =
if in.token == IMPLICIT then addParamMod(() => Mod.Implicit())
else
false
if in.token == GIVEN || isIdent(nme.using) then addParamMod(() => Mod.Given())
if in.token == ERASED then addParamMod(() => Mod.Erased())

def param(): ValDef = {
val start = in.offset
Expand Down Expand Up @@ -2984,32 +2972,22 @@ object Parsers {
val clause =
if prefix then param() :: Nil
else
if !impliedModOpt(IMPLICIT, () => Mod.Implicit()) then
impliedModOpt(GIVEN, () => Mod.Given())
impliedModOpt(ERASED, () => Mod.Erased())
paramMods()
if givenOnly && !impliedMods.is(Given) then
syntaxError("Normal parameter clause cannot follow context parameter clause")
syntaxError("`using` expected")
val isParams =
!impliedMods.is(Given)
|| startParamTokens.contains(in.token)
|| isIdent && (in.name == nme.inline || in.lookaheadIn(BitSet(COLON)))
if isParams then commaSeparated(() => param())
else givenTypes(typ, nparams, ofClass)
else contextTypes(ofClass, nparams)
checkVarArgsRules(clause)
clause
}
}

/** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
* | ClsParamClause ClsParamClauses
* | ClsParamClauses1
* ClsParamClauses1 ::= WithClsParamClause ClsParamClauses
* | AnnotTypes ClsParamClauses1ClsParamClauses
* DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
* | DefParamClause DefParamClauses
* | DefParamClauses1
* DefParamClauses1 ::= WithCaramClause DefParamClauses
* | AnnotTypes DeParamClauses1
*
* @return The parameter definitions
*/
Expand All @@ -3019,33 +2997,18 @@ object Parsers {

def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] =
newLineOptWhenFollowedBy(LPAREN)
val prefixMods =
if in.token == WITH && in.ch != Chars.LF then // TODO: remove LF test
in.nextToken()
Modifiers(Given)
else
EmptyModifiers
if in.token == LPAREN then
val paramsStart = in.offset
val params = paramClause(
nparams,
ofClass = ofClass,
ofCaseClass = ofCaseClass,
givenOnly = givenOnly,
firstClause = firstClause,
prefixMods = prefixMods)
firstClause = firstClause)
val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit)
val isGivenClause = prefixMods.is(Given)
|| params.nonEmpty && params.head.mods.flags.is(Given)
params :: (
if lastClause then Nil
else recur(firstClause = false, nparams + params.length))
else if prefixMods.is(Given) then
val params = givenTypes(annotType, nparams, ofClass)
params :: (
if in.token == WITH then recur(firstClause = false, nparams + params.length)
else Nil
)
else Nil
end recur

Expand Down Expand Up @@ -3090,8 +3053,8 @@ object Parsers {

/** ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
* | WildCardSelector {‘,’ WildCardSelector}
* WildCardSelector ::= ‘given’ [InfixType]
* | ‘_' [‘:’ InfixType]
* WildCardSelector ::= ‘given’ (‘_' | InfixType)
* | ‘_'
*/
def importSelectors(idOK: Boolean): List[ImportSelector] =
val selToken = in.token
Expand Down Expand Up @@ -3543,10 +3506,7 @@ object Parsers {

/** GivenDef ::= [GivenSig] [‘_’ ‘<:’] Type ‘=’ Expr
* | [GivenSig] ConstrApps [TemplateBody]
* GivenSig ::= [id] [DefTypeParamClause] {WithParamsOrTypes} ‘as’
* ExtParamClause ::= [DefTypeParamClause] DefParamClause
* ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
* WithParamsOrTypes ::= WithParamClause | AnnotTypes
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘as’
*/
def givenDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
var mods1 = addMod(mods, instanceMod)
Expand All @@ -3571,18 +3531,17 @@ object Parsers {
templ.body.foreach(checkExtensionMethod(tparams, _))
ModuleDef(name, templ)
else
val hasLabel = !name.isEmpty && in.token == COLON || in.isIdent(nme.as)
val hasLabel = !name.isEmpty && in.token == COLON || isIdent(nme.as)
if hasLabel then in.nextToken()
val tparams = typeParamClauseOpt(ParamOwner.Def)
val paramsStart = in.offset
val vparamss =
if in.token == WITH && in.ch != Chars.LF // TODO: remove LF test
|| in.token == LPAREN && followingIsParamOrGivenType()
if in.token == LPAREN && followingIsParamOrGivenType()
then paramClauses()
else Nil
def checkAllGivens(vparamss: List[List[ValDef]], what: String) =
vparamss.foreach(_.foreach(vparam =>
if !vparam.mods.is(Given) then syntaxError(em"$what must be `given`", vparam.span)))
if !vparam.mods.is(Given) then syntaxError(em"$what must be preceded by `using`", vparam.span)))
checkAllGivens(vparamss, "parameter of given instance")
val parents =
if in.token == SUBTYPE && !hasLabel then
Expand Down Expand Up @@ -3620,7 +3579,7 @@ object Parsers {
finalizeDef(gdef, mods1, start)
}

/** ExtensionDef ::= [id] ‘on’ ExtParamClause GivenParamClauses ExtMethods
/** ExtensionDef ::= [id] ‘on’ ExtParamClause {UsingParamClause} ExtMethods
*/
def extensionDef(start: Offset, mods: Modifiers): ModuleDef =
in.nextToken()
Expand All @@ -3644,8 +3603,7 @@ object Parsers {
val constrApp: () => Tree = () => {
val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR))
// Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code
if in.token == LPAREN || in.token == DOTWITH then parArgumentExprss(wrapNew(t))
else t
if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t
}

/** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp}
Expand Down
10 changes: 1 addition & 9 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Expand Up @@ -593,11 +593,7 @@ object Scanners {
this.copyFrom(prev)
}

/** - Join CASE + CLASS => CASECLASS,
* CASE + OBJECT => CASEOBJECT,
* SEMI + ELSE => ELSE,
* COLON + <EOL> => COLONEOL
* DOT + WITH => DOTWITH
/** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE, COLON + <EOL> => COLONEOL
* - Insert missing OUTDENTs at EOF
*/
def postProcessToken(): Unit = {
Expand All @@ -623,10 +619,6 @@ object Scanners {
} else if (token == EOF) { // e.g. when the REPL is parsing "val List(x, y, _*,"
/* skip the trailing comma */
} else reset()
case DOT =>
lookahead()
if token == WITH then fuse(DOTWITH)
else reset()
case COLON =>
if colonSyntax then observeColonEOL()
case EOF | RBRACE =>
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Expand Up @@ -204,7 +204,6 @@ object Tokens extends TokensCommon {
final val QUOTE = 87; enter(QUOTE, "'")

final val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")
final val DOTWITH = 89; enter(DOTWITH, ".with")

/** XML mode */
final val XMLSTART = 98; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
Expand Down