Skip to content

Commit

Permalink
Update syntax.md (scala#19670)
Browse files Browse the repository at this point in the history
 1. Add missing soft modifiers
2. Port changes from multiple type parameter sections to reference. Now
that multiple type parameter sections are merged, this is the official
syntax. This also covers extension method declarations.

Fixes scala#19667
Fixes scala#19668
  • Loading branch information
odersky authored Feb 14, 2024
2 parents 8776677 + f2f5051 commit b160bbb
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 140 deletions.
178 changes: 88 additions & 90 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1532,13 +1532,15 @@ object Parsers {
* PolyFunType ::= HKTypeParamClause '=>' Type
* | HKTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions
* FunTypeArgs ::= InfixType
* | `(' [ [ ‘['erased'] FunArgType {`,' FunArgType } ] `)'
* | '(' [ ‘['erased'] TypedFunParam {',' TypedFunParam } ')'
* | `(' [ FunArgType {`,' FunArgType } ] `)'
* | '(' [ TypedFunParam {',' TypedFunParam } ')'
* MatchType ::= InfixType `match` <<< TypeCaseClauses >>>
*/
def typ(): Tree =
val start = in.offset
var imods = Modifiers()
var erasedArgs: ListBuffer[Boolean] = ListBuffer()
val erasedArgs: ListBuffer[Boolean] = ListBuffer()

def functionRest(params: List[Tree]): Tree =
val paramSpan = Span(start, in.lastOffset)
atSpan(start, in.offset) {
Expand Down Expand Up @@ -1567,7 +1569,8 @@ object Parsers {
else
accept(ARROW)

val resultType = if isPure then capturesAndResult(typ) else typ()
val resultType =
if isPure then capturesAndResult(typ) else typ()
if token == TLARROW then
for case ValDef(_, tpt, _) <- params do
if isByNameType(tpt) then
Expand All @@ -1585,98 +1588,93 @@ object Parsers {
Function(params, resultType)
}

var isValParamList = false
def typeRest(t: Tree) = in.token match
case ARROW | CTXARROW =>
erasedArgs.addOne(false)
functionRest(t :: Nil)
case MATCH =>
matchType(t)
case FORSOME =>
syntaxError(ExistentialTypesNoLongerSupported())
t
case _ if isPureArrow =>
erasedArgs.addOne(false)
functionRest(t :: Nil)
case _ =>
if erasedArgs.contains(true) && !t.isInstanceOf[FunctionWithMods] then
syntaxError(ErasedTypesCanOnlyBeFunctionTypes(), implicitKwPos(start))
t

val t =
if (in.token == LPAREN) {
var isValParamList = false
if in.token == LPAREN then
in.nextToken()
if in.token == RPAREN then
in.nextToken()
if (in.token == RPAREN) {
in.nextToken()
functionRest(Nil)
}
else {
val paramStart = in.offset
def addErased() =
erasedArgs.addOne(isErasedKw)
if isErasedKw then { in.skipToken(); }
addErased()
val ts = in.currentRegion.withCommasExpected {
functionRest(Nil)
else
val paramStart = in.offset
def addErased() =
erasedArgs.addOne(isErasedKw)
if isErasedKw then in.skipToken()
addErased()
val args =
in.currentRegion.withCommasExpected:
funArgType() match
case Ident(name) if name != tpnme.WILDCARD && in.isColon =>
isValParamList = true
def funParam(start: Offset, mods: Modifiers) = {
atSpan(start) {
def funParam(start: Offset, mods: Modifiers) =
atSpan(start):
addErased()
typedFunParam(in.offset, ident(), imods)
}
}
commaSeparatedRest(
typedFunParam(paramStart, name.toTermName, imods),
() => funParam(in.offset, imods))
case t =>
def funParam() = {
addErased()
funArgType()
}
commaSeparatedRest(t, funParam)
}
accept(RPAREN)
if isValParamList || in.isArrow || isPureArrow then
functionRest(ts)
else {
val ts1 = ts.mapConserve { t =>
if isByNameType(t) then
syntaxError(ByNameParameterNotSupported(t), t.span)
stripByNameType(t)
else
t
}
val tuple = atSpan(start) { makeTupleOrParens(ts1) }
infixTypeRest(
refinedTypeRest(
withTypeRest(
annotTypeRest(
simpleTypeRest(tuple)))))
}
}
}
else if (in.token == LBRACKET) {
val start = in.offset
val tparams = typeParamClause(ParamOwner.TypeParam)
if (in.token == TLARROW)
atSpan(start, in.skipToken())(LambdaTypeTree(tparams, toplevelTyp()))
else if (in.token == ARROW || isPureArrow(nme.PUREARROW)) {
val arrowOffset = in.skipToken()
val body = toplevelTyp()
atSpan(start, arrowOffset) {
getFunction(body) match {
case Some(f) =>
PolyFunction(tparams, body)
case None =>
syntaxError(em"Implementation restriction: polymorphic function types must have a value parameter", arrowOffset)
Ident(nme.ERROR.toTypeName)
}
}
}
else { accept(TLARROW); typ() }
}
else if (in.token == INDENT) enclosed(INDENT, typ())
else infixType()

in.token match
case ARROW | CTXARROW =>
erasedArgs.addOne(false)
functionRest(t :: Nil)
case MATCH => matchType(t)
case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t
case _ =>
if isPureArrow then
erasedArgs.addOne(false)
functionRest(t :: Nil)
def funArg() =
erasedArgs.addOne(false)
funArgType()
commaSeparatedRest(t, funArg)
accept(RPAREN)
if isValParamList || in.isArrow || isPureArrow then
functionRest(args)
else
if (erasedArgs.contains(true) && !t.isInstanceOf[FunctionWithMods])
syntaxError(ErasedTypesCanOnlyBeFunctionTypes(), implicitKwPos(start))
t
val args1 = args.mapConserve: t =>
if isByNameType(t) then
syntaxError(ByNameParameterNotSupported(t), t.span)
stripByNameType(t)
else
t
val tuple = atSpan(start):
makeTupleOrParens(args1)
typeRest:
infixTypeRest:
refinedTypeRest:
withTypeRest:
annotTypeRest:
simpleTypeRest(tuple)
else if in.token == LBRACKET then
val start = in.offset
val tparams = typeParamClause(ParamOwner.TypeParam)
if in.token == TLARROW then
atSpan(start, in.skipToken()):
LambdaTypeTree(tparams, toplevelTyp())
else if in.token == ARROW || isPureArrow(nme.PUREARROW) then
val arrowOffset = in.skipToken()
val body = toplevelTyp()
atSpan(start, arrowOffset):
getFunction(body) match
case Some(f) =>
PolyFunction(tparams, body)
case None =>
syntaxError(em"Implementation restriction: polymorphic function types must have a value parameter", arrowOffset)
Ident(nme.ERROR.toTypeName)
else
accept(TLARROW)
typ()
else if in.token == INDENT then
enclosed(INDENT, typ())
else
typeRest(infixType())
end typ

private def makeKindProjectorTypeDef(name: TypeName): TypeDef = {
Expand Down Expand Up @@ -1713,7 +1711,7 @@ object Parsers {
private def implicitKwPos(start: Int): Span =
Span(start, start + nme.IMPLICITkw.asSimpleName.length)

/** TypedFunParam ::= id ':' Type */
/** TypedFunParam ::= [`erased`] id ':' Type */
def typedFunParam(start: Offset, name: TermName, mods: Modifiers = EmptyModifiers): ValDef =
atSpan(start) {
acceptColon()
Expand Down Expand Up @@ -2068,7 +2066,7 @@ object Parsers {
*/
def paramType(): Tree = paramTypeOf(paramValueType)

/** ParamValueType ::= [`into`] Type [`*']
/** ParamValueType ::= Type [`*']
*/
def paramValueType(): Tree = {
val t = maybeInto(toplevelTyp)
Expand Down Expand Up @@ -2425,7 +2423,7 @@ object Parsers {
Match(t, inBracesOrIndented(caseClauses(() => caseClause())))
}

/** `match' `{' TypeCaseClauses `}'
/** `match' <<< TypeCaseClauses >>>
*/
def matchType(t: Tree): MatchTypeTree =
atSpan(startOffset(t), accept(MATCH)) {
Expand All @@ -2435,7 +2433,7 @@ object Parsers {
/** FunParams ::= Bindings
* | id
* | `_'
* Bindings ::= `(' [[‘erased’] Binding {`,' Binding}] `)'
* Bindings ::= `(' [Binding {`,' Binding}] `)'
*/
def funParams(mods: Modifiers, location: Location): List[Tree] =
if in.token == LPAREN then
Expand Down Expand Up @@ -3173,7 +3171,7 @@ object Parsers {
* | AccessModifier
* | override
* | opaque
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased | inline | transparent
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | inline | transparent | infix | erased
*/
def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = {
@tailrec
Expand Down Expand Up @@ -3326,7 +3324,7 @@ object Parsers {
/** ClsTermParamClause ::= ‘(’ ClsParams ‘)’ | UsingClsTermParamClause
* UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’
* ClsParams ::= ClsParam {‘,’ ClsParam}
* ClsParam ::= {Annotation}
* ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’)] Param
*
* TypelessClause ::= DefTermParamClause
* | UsingParamClause
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/transform/Recheck.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import ast.*
import Names.Name
import Phases.Phase
import DenotTransformers.{DenotTransformer, IdentityDenotTransformer, SymTransformer}
import NamerOps.{methodType, linkConstructorParams}
import NamerOps.linkConstructorParams
import NullOpsDecorator.stripNull
import typer.ErrorReporting.err
import typer.ProtoTypes.*
import typer.TypeAssigner.seqLitType
import typer.ConstFold
import typer.ErrorReporting.{Addenda, NothingToAdd}
import NamerOps.methodType
import config.Printers.recheckr
import util.Property
import StdNames.nme
Expand Down
39 changes: 25 additions & 14 deletions docs/_docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ productions map to AST nodes.
The following description of Scala tokens uses literal characters `‘c’` when
referring to the ASCII fragment `\u0000``\u007F`.

Informal descriptions are typeset as `“some comment”`.

## Lexical Syntax

The lexical syntax of Scala is given by the following grammar in EBNF form:
Expand Down Expand Up @@ -99,7 +101,10 @@ semi ::= ‘;’ | nl {nl}

## Optional Braces

The lexical analyzer also inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](../reference/other-new-features/indentation.md)
The principle of optional braces is that any keyword that can be followed by `{` can also be followed by an indented block, without needing an intervening `:`.
(Allowing an optional `:` would be counterproductive since it would introduce several ways to do the same thing.)

The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](../reference/other-new-features/indentation.md).

In the context-free productions below we use the notation `<<< ts >>>`
to indicate a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent`. Analogously, the
Expand Down Expand Up @@ -201,14 +206,13 @@ SimpleType1 ::= id
Singleton ::= SimpleRef
| SimpleLiteral
| Singleton ‘.’ id
FunArgType ::= [`erased`] Type
| [`erased`] ‘=>’ Type PrefixOp(=>, t)
FunArgType ::= Type
| ‘=>’ Type PrefixOp(=>, t)
FunArgTypes ::= FunArgType { ‘,’ FunArgType }
ParamType ::= [‘=>’] ParamValueType
ParamValueType ::= [‘into’] ExactParamType Into(t)
ExactParamType ::= ParamValueType [‘*’] PostfixOp(t, "*")
ParamValueType ::= Type [‘*’] PostfixOp(t, "*")
TypeArgs ::= ‘[’ Types ‘]’ ts
Refinement ::= :<<< [RefineDef] {semi [RefineDef]} >>> ds
Refinement ::= :<<< [RefineDcl] {semi [RefineDcl]} >>> ds
TypeBounds ::= [‘>:’ Type] [‘<:’ Type] TypeBoundsTree(lo, hi)
TypeParamBounds ::= TypeBounds {‘:’ Type} ContextBounds(typeBounds, tps)
Types ::= Type {‘,’ Type}
Expand All @@ -223,7 +227,7 @@ BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block
| HkTypeParamClause ‘=>’ Block
| Expr1
FunParams ::= Bindings
| [`erased`] id
| id
| ‘_’
Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] If(Parens(cond), thenp, elsep?)
| [‘inline’] ‘if’ Expr ‘then’ Expr [[semi] ‘else’ Expr] If(cond, thenp, elsep?)
Expand Down Expand Up @@ -272,7 +276,7 @@ ColonArgument ::= colon [LambdaStart]
LambdaStart ::= FunParams (‘=>’ | ‘?=>’)
| HkTypeParamClause ‘=>’
Quoted ::= ‘'’ ‘{’ Block ‘}’
| ‘'’ ‘[’ Type ‘]’
| ‘'’ ‘[’ TypeBlock ‘]’
ExprSplice ::= spliceId -- if inside quoted block
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
| ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern
Expand All @@ -294,6 +298,8 @@ BlockStat ::= Import
| Extension
| Expr1
| EndMarker
TypeBlock ::= {TypeBlockStat semi} Type
TypeBlockStat ::= ‘type’ {nl} TypeDef
ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr ForYield(enums, expr) / ForDo(enums, expr)
| ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr
Expand Down Expand Up @@ -353,7 +359,7 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
| [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’
ClsParams ::= ClsParam {‘,’ ClsParam}
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
[{Modifier} (‘val’ | ‘var’)] Param
DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent
DefParamClause ::= DefTypeParamClause
Expand All @@ -376,8 +382,8 @@ Param ::= id ‘:’ ParamType [‘=’ Expr]

### Bindings and Imports
```ebnf
Bindings ::= ‘(’[`erased`] [Binding {‘,’ [`erased`] Binding}] ‘)’
Binding ::= (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree)
Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’
Binding ::= [`erased`] (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree)
Modifier ::= LocalModifier
| AccessModifier
Expand All @@ -390,6 +396,10 @@ LocalModifier ::= ‘abstract’
| ‘implicit’
| ‘lazy’
| ‘inline’
| ‘transparent’
| ‘infix’
| ‘erased’
AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier]
AccessQualifier ::= ‘[’ id ‘]’
Expand All @@ -414,9 +424,11 @@ EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ |

### Definitions
```ebnf
RefineDef ::= ‘val’ ValDef
| ‘def’ DefDef
RefineDcl ::= ‘val’ ValDcl
| ‘def’ DefDcl
| ‘type’ {nl} TypeDef
ValDcl ::= ids ‘:’ Type
DefDcl ::= DefSig ‘:’ Type
Def ::= ‘val’ PatDef
| ‘var’ PatDef
Expand Down Expand Up @@ -461,7 +473,6 @@ TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>>
TemplateStat ::= Import
| Export
| {Annotation [nl]} {Modifier} Def
| {Annotation [nl]} {Modifier} Dcl
| Extension
| Expr1
| EndMarker
Expand Down
Loading

0 comments on commit b160bbb

Please sign in to comment.