
scalariform.parser.InferredSemicolonScalaParser.scala Maven / Gradle / Ivy
The newest version!
package scalariform.parser
import scalariform.lexer.Tokens._
import scalariform.lexer._
import scalariform.utils.Utils._
import scala.collection.mutable.ListBuffer
import PartialFunction._
object InferredSemicolonScalaParser {
def findSemicolons(tokens: Array[Token]) = {
val parser = new InferredSemicolonScalaParser(tokens)
parser.safeParse(parser.compilationUnitOrScript)
parser.inferredSemicolons
}
}
class InferredSemicolonScalaParser(tokens: Array[Token]) {
private val logging: Boolean = false
val forgiving = true
def safeParse[T](production: ⇒ T): Option[T] = try Some(production) catch { case e: ScalaParserException ⇒ None }
require(!tokens.isEmpty) // at least EOF
def inParens[T](body: ⇒ T) {
accept(LPAREN)
body
accept(RPAREN)
}
def inBraces[T](body: ⇒ T) {
accept(LBRACE)
body
accept(RBRACE)
}
def inBrackets[T](body: ⇒ T) {
accept(LBRACKET)
body
accept(RBRACKET)
}
def makeParens[T](body: ⇒ T) = inParens { if (RPAREN) None else Some(body) }
def compilationUnitOrScript() {
val originalPos = pos
try {
compilationUnit()
} catch {
case e: ScalaParserException ⇒
pos = originalPos
if (logging) println("Rewinding to try alternative: " + currentToken)
try {
scriptBody()
} catch { case e2: ScalaParserException ⇒ throw e }
}
}
def scriptBody() {
templateStatSeq()
accept(EOF)
}
private def accept(tokenType: TokenType): Token =
if (currentTokenType == tokenType)
nextToken()
else
throw new ScalaParserException("Expected token " + tokenType + " but got " + currentToken)
var inferredSemicolons: Set[Token] = Set()
private def acceptStatSep(): Token = currentTokenType match {
case NEWLINE | NEWLINES ⇒
val token = nextToken()
inferredSemicolons = inferredSemicolons + token
token
case _ ⇒ accept(SEMI)
}
private def acceptStatSepOpt() = if (!isStatSeqEnd) acceptStatSep
private def isModifier = currentTokenType match {
case ABSTRACT | FINAL | SEALED | PRIVATE |
PROTECTED | OVERRIDE | IMPLICIT | LAZY ⇒ true
case _ ⇒ false
}
private def isLocalModifier: Boolean = currentTokenType match {
case ABSTRACT | FINAL | SEALED | IMPLICIT | LAZY ⇒ true
case _ ⇒ false
}
private def isTemplateIntro: Boolean = currentTokenType match {
case OBJECT | CLASS | TRAIT ⇒ true
case CASE if caseObject ⇒ true
case CASE if caseClass ⇒ true
case _ ⇒ false
}
private def isDclIntro: Boolean = currentTokenType match {
case VAL | VAR | DEF | TYPE ⇒ true
case _ ⇒ false
}
private def isDefIntro: Boolean = isTemplateIntro || isDclIntro
private def isNumericLit: Boolean = currentTokenType match {
case INTEGER_LITERAL | FLOATING_POINT_LITERAL ⇒ true
case _ ⇒ false
}
private def isUnaryOp: Boolean = currentTokenType match {
case MINUS | PLUS | TILDE | EXCLAMATION ⇒ true
case _ ⇒ false
}
private def isIdent: Boolean = isIdent(currentTokenType)
private def isIdent(tokenType: TokenType) = tokenType match {
case VARID | OTHERID | PLUS | MINUS | STAR | PIPE | TILDE | EXCLAMATION ⇒ true
case _ ⇒ false
}
private def isLiteralToken(tokenType: TokenType): Boolean = tokenType match {
case CHARACTER_LITERAL | INTEGER_LITERAL | FLOATING_POINT_LITERAL |
STRING_LITERAL | SYMBOL_LITERAL | TRUE | FALSE | NULL ⇒ true
case _ ⇒ false
}
private def isLiteral = isLiteralToken(currentTokenType)
private def isExprIntroToken(tokenType: TokenType) =
isLiteralToken(tokenType) || (tokenType match {
case THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE |
DO | RETURN | THROW | LPAREN | LBRACE ⇒ true
case XML_START_OPEN | XML_UNPARSED | XML_COMMENT | XML_CDATA | XML_PROCESSING_INSTRUCTION ⇒ true
case _ if isIdent(tokenType) ⇒ true
case _ ⇒ false
})
private def isExprIntro: Boolean = isExprIntroToken(currentTokenType)
private def isTypeIntroToken(tokenType: TokenType): Boolean = tokenType match {
case THIS | SUPER | USCORE | LPAREN | AT ⇒ true
case _ if isIdent(tokenType) ⇒ true
case _ ⇒ false
}
private def isTypeIntro: Boolean = isTypeIntroToken(currentTokenType)
private def isStatSeqEnd = RBRACE || EOF
private def isStatSep(tokenType: TokenType) =
tokenType == NEWLINE || tokenType == NEWLINES || tokenType == SEMI
private def isStatSep: Boolean = isStatSep(currentTokenType)
private def tokenSeparated[T](separator: TokenType, sepFirst: Boolean, part: ⇒ T) {
if (sepFirst) () else part
while (separator) {
nextToken()
part
}
}
private def commaSeparated[T](part: ⇒ T) =
tokenSeparated(COMMA, false, part)
private def caseSeparated[T](part: ⇒ T) = tokenSeparated(CASE, true, part)
private def readAnnots[T](part: ⇒ T) = tokenSeparated(AT, true, part)
trait PatternContextSensitive {
def argType()
def functionArgType()
private def tupleInfixType() {
nextToken()
if (RPAREN) {
nextToken()
accept(ARROW)
typ()
} else {
functionTypes()
accept(RPAREN)
if (ARROW) {
nextToken()
typ()
} else {
simpleTypeRest()
annotTypeRest()
compoundTypeRest()
infixTypeRest()
}
}
}
private def makeExistentialTypeTree() = refinement()
def typ() {
if (LPAREN) tupleInfixType()
else infixType()
currentTokenType match {
case ARROW ⇒
nextToken()
typ()
case FORSOME ⇒
nextToken()
makeExistentialTypeTree()
case _ ⇒
}
}
def typeArgs() = {
inBrackets(types())
}
def annotType() = {
simpleType()
annotTypeRest()
}
def simpleType() = {
currentTokenType match {
case LPAREN ⇒
inParens(types())
case USCORE ⇒
nextToken()
wildcardType()
case _ ⇒
path(thisOK = false, typeOK = true)
}
simpleTypeRest()
}
private def typeProjection() = {
nextToken()
ident()
}
private def simpleTypeRest() {
currentTokenType match {
case HASH ⇒
typeProjection()
simpleTypeRest()
case LBRACKET ⇒
typeArgs()
simpleTypeRest()
case _ ⇒
Nil
}
}
def compoundType() = {
if (LBRACE) None else Some(annotType())
compoundTypeRest()
}
private def compoundTypeRest() = {
while (WITH) {
nextToken()
annotType()
}
newLineOptWhenFollowedBy(LBRACE)
if (LBRACE) Some(refinement()) else None
}
def infixTypeRest() {
if (isIdent && !STAR) {
val identToken = currentToken
InfixTypeConstructor(ident())
newLineOptWhenFollowing(isTypeIntroToken)
if (isLeftAssoc(identToken)) {
compoundType()
infixTypeRest()
} else {
infixType()
}
} else
Nil
}
def infixType() = {
compoundType()
infixTypeRest()
}
private def types() =
commaSeparated(argType())
private def functionTypes() =
commaSeparated(functionArgType())
}
private def ident() =
if (isIdent)
nextToken()
else
throw new ScalaParserException("Expected identifier, but got " + currentToken)
private def selector() = ident()
private def pathC(thisOK: Boolean, typeOK: Boolean) {
if (THIS) {
nextToken()
if (!thisOK || DOT) {
accept(DOT)
selectors(null, typeOK)
}
} else if (SUPER) {
nextToken()
mixinQualifierOpt()
accept(DOT)
selector()
if (DOT) {
nextToken()
selectors(null, typeOK)
}
} else {
ident()
if (DOT) {
nextToken()
if (THIS) {
nextToken()
if (!thisOK || DOT) {
accept(DOT)
selectors(null, typeOK)
}
} else if (SUPER) {
nextToken()
mixinQualifierOpt()
accept(DOT)
selector()
if (DOT) {
nextToken()
selectors(null, typeOK)
}
} else
selectors(null, typeOK)
}
}
}
private def path(thisOK: Boolean, typeOK: Boolean) = pathC(thisOK, typeOK)
private def selectors(delme: String, typeOK: Boolean) {
if (typeOK && TYPE)
nextToken()
else {
selector()
if (DOT) {
nextToken()
selectors(null, typeOK)
}
}
}
private def mixinQualifierOpt() {
if (LBRACKET) inBrackets(ident)
}
private def stableId() = path(thisOK = false, typeOK = false)
private def qualId() = {
ident()
if (DOT) {
nextToken()
selectors(null, typeOK = false)
}
}
private def pkgQualId() = {
qualId()
newLineOptWhenFollowedBy(LBRACE)
}
private def literal(): Token =
if (CHARACTER_LITERAL || INTEGER_LITERAL || FLOATING_POINT_LITERAL || STRING_LITERAL || SYMBOL_LITERAL || TRUE || FALSE || NULL)
nextToken()
else
throw new ScalaParserException("illegal literal: " + currentToken)
private def newLineOpt() { if (NEWLINE) nextToken() }
private def newLinesOpt() = if (NEWLINE || NEWLINES) nextToken()
private def newLineOptWhenFollowedBy(tokenType: TokenType) =
if (NEWLINE && lookahead(1) == tokenType)
newLineOpt()
else
None
private def newLineOptWhenFollowing(pred: TokenType ⇒ Boolean) =
if (NEWLINE && pred(lookahead(1)))
newLineOpt()
else
None
private def typedOpt() =
if (COLON) {
nextToken()
typ()
}
private def typeOrInfixType(location: Location) =
if (location == Local)
typ()
else
startInfixType()
private def annotTypeRest() =
annotations(skipNewLines = false)
private def wildcardType() = {
typeBounds()
}
private def equalsExpr() = {
accept(EQUALS)
expr()
}
private def condExpr() = {
if (LPAREN) {
nextToken()
expr()
accept(RPAREN)
} else {
accept(LPAREN)
throw new ScalaParserException("Straggling lparen thing")
}
}
private def statement(location: Location) = expr(location)
def expr() { expr(Local) }
private def expr(location: Location) {
expr0(location)
}
private def expr0(location: Location) {
currentTokenType match {
case IF ⇒
nextToken()
condExpr()
newLinesOpt()
expr()
if (SEMI && lookahead(1) == ELSE) Some(nextToken()) else None
if (ELSE) {
nextToken()
expr()
}
case TRY ⇒
nextToken()
currentTokenType match {
case LBRACE ⇒
inBraces(block())
case LPAREN ⇒ inParens(expr())
case _ ⇒ expr
}
val catchClauseOption =
if (!CATCH)
None
else {
nextToken()
if (!LBRACE)
expr()
else {
inBraces {
if (CASE)
caseClauses()
else
expr()
}
}
}
currentTokenType match {
case FINALLY ⇒
nextToken()
expr()
case _ ⇒
None
}
case WHILE ⇒
nextToken()
condExpr()
newLinesOpt()
expr()
case DO ⇒
nextToken()
expr()
if (isStatSep) acceptStatSep() // <-- for inferred semi // nextToken()
accept(WHILE)
condExpr()
case FOR ⇒
nextToken()
val (open, close) = if (LBRACE) (LBRACE, RBRACE) else (LPAREN, RPAREN)
if (LBRACE) inBraces(enumerators())
else inParens(enumerators())
newLinesOpt()
if (YIELD) {
nextToken()
expr()
} else
expr()
case RETURN ⇒
nextToken()
if (isExprIntro) expr()
case THROW ⇒
nextToken()
expr()
case IMPLICIT ⇒
val implicitToken = nextToken()
List(implicitClosure(location))
case _ ⇒
postfixExpr()
if (EQUALS) {
optional { /* TODO: case Ident(_) | Select(_, _) | Apply(_, _) => */
(accept(EQUALS), expr())
}
} else if (COLON) {
nextToken()
if (USCORE) {
nextToken()
accept(STAR)
} else if (AT) {
annotations(skipNewLines = false)
} else {
typeOrInfixType(location)
}
} else if (MATCH) {
nextToken()
inBraces(caseClauses())
}
val lhsIsTypedParamList = false // TODO!
if (ARROW && (location != InTemplate || lhsIsTypedParamList)) {
optional {
nextToken()
if (location != InBlock) expr() else block()
}
}
}
}
private def implicitClosure(location: Location) {
ident()
if (COLON) {
nextToken()
typeOrInfixType(location)
}
accept(ARROW)
if (location != InBlock) expr() else block()
}
private final val otherLetters = Set[Char]('\u0024', '\u005F') // '$' and '_'
private final val letterGroups = {
import java.lang.Character._
Set[Byte](LOWERCASE_LETTER, UPPERCASE_LETTER, OTHER_LETTER, TITLECASE_LETTER, LETTER_NUMBER)
}
private def isScalaLetter(ch: Char) = letterGroups(java.lang.Character.getType(ch).toByte) || otherLetters(ch)
private def isOpAssignmentName(name: String) = name match {
case "!=" | "<=" | ">=" | "" ⇒ false
case _ ⇒ name.endsWith("=") && !name.startsWith("=") && ScalaOnlyLexer.isOperatorPart(name(0))
}
private def postfixExpr() {
prefixExpr()
while (isIdent) {
ident()
newLineOptWhenFollowing(isExprIntroToken)
if (isExprIntro) {
prefixExpr()
}
}
}
private def prefixExpr() {
if (isUnaryOp) {
val isMinus = MINUS
ident()
if (isMinus && isNumericLit) {
literal()
simpleExprRest(true)
} else
simpleExpr()
} else
simpleExpr()
}
private def simpleExpr() {
var canApply = true
if (isLiteral) literal()
else currentTokenType match {
case XML_START_OPEN | XML_COMMENT | XML_CDATA | XML_UNPARSED | XML_PROCESSING_INSTRUCTION ⇒
xmlLiteral()
case VARID | OTHERID | PLUS | MINUS | STAR | PIPE | TILDE | EXCLAMATION | THIS | SUPER ⇒
pathC(thisOK = true, typeOK = false)
case USCORE ⇒
nextToken()
case LPAREN ⇒
makeParens(commaSeparated(expr))
case LBRACE ⇒
canApply = false
blockExpr()
case NEW ⇒
canApply = false
nextToken()
template(isTrait = false)
case _ ⇒
throw new ScalaParserException("illegal start of simple expression: " + currentToken)
}
simpleExprRest(canApply)
}
private def simpleExprRest(canApply: Boolean) {
val newLineOpt = if (canApply) newLineOptWhenFollowedBy(LBRACE) else None
currentTokenType match {
case DOT ⇒
nextToken()
selector()
simpleExprRest(canApply = true)
case LBRACKET ⇒
val identifierCond = true // TODO: missing check: case Ident(_) | Select(_, _) => OK, just means we accept multiple type param [X][Y] clauses
if (identifierCond) {
exprTypeArgs()
simpleExprRest(canApply = true)
}
case LPAREN | LBRACE if canApply ⇒
argumentExprs()
simpleExprRest(canApply = true)
case USCORE ⇒
nextToken()
case _ ⇒
}
}
private def argumentExprs() {
def argument() = expr()
def args() = commaSeparated(argument())
currentTokenType match {
case LBRACE ⇒ blockExpr()
case LPAREN ⇒
inParens { if (RPAREN) Nil else args() }
case _ ⇒
}
}
private def multipleArgumentExprs() {
if (!LPAREN) Nil
else { argumentExprs(); multipleArgumentExprs() }
}
private def blockExpr() {
inBraces {
if (justCase) caseClauses()
else block()
}
}
private def block() { blockStatSeq() }
private def caseClauses() {
caseSeparated {
(pattern(), guard(), caseBlock())
}
// TODO:
// if (caseClauses_.isEmpty)
// accept(CASE)
}
private def caseBlock() {
accept(ARROW)
block()
}
private def guard() {
if (IF) {
nextToken()
postfixExpr()
}
}
private def enumerators() {
val newStyle = !VAL
generator(eqOK = false)
while (isStatSep) {
acceptStatSep() // <-- for inferred semi //nextToken()
if (newStyle) {
if (IF) guard()
else generator(eqOK = true)
} else {
if (VAL) generator(eqOK = true)
else expr()
}
}
}
private def generator(eqOK: Boolean) {
if (VAL) nextToken()
noSeq.pattern1()
if (EQUALS && eqOK) nextToken() else accept(LARROW)
expr()
while (IF) guard()
}
trait SeqContextSensitive extends PatternContextSensitive {
def interceptStarPattern()
def functionArgType() = argType()
def argType() {
currentTokenType match {
case USCORE ⇒
nextToken()
if (SUBTYPE || SUPERTYPE) wildcardType()
case _ if isIdent && isVariableName(currentToken.getText) ⇒
ident()
case _ ⇒
typ()
}
}
def patterns() {
commaSeparated(pattern())
}
def pattern() { // Scalac now uses a loop() method, but this is still OK:
pattern1()
if (PIPE)
while (PIPE) {
nextToken()
pattern1()
}
}
def pattern1() {
pattern2()
if (COLON) { // TODO: case Ident(name) if (treeInfo.isVarPattern(p) && in.token == COLON)
nextToken()
compoundType()
}
}
def pattern2() {
pattern3()
if (AT) {
// TODO: Compare Parsers.scala
optional {
nextToken()
pattern3()
}
}
}
def pattern3() {
simplePattern()
interceptStarPattern()
while (isIdent && !PIPE) {
ident()
simplePattern()
}
}
def simplePattern() {
currentTokenType match {
case VARID | OTHERID | PLUS | MINUS | STAR | PIPE | TILDE | EXCLAMATION | THIS ⇒
val nameIsMinus: Boolean = MINUS // TODO case Ident(name) if name == nme.MINUS =>
stableId()
condOpt(currentTokenType) {
case INTEGER_LITERAL | FLOATING_POINT_LITERAL if nameIsMinus ⇒ literal()
}
if (LBRACKET) typeArgs()
else None
if (LPAREN) argumentPatterns()
case USCORE ⇒
nextToken()
case CHARACTER_LITERAL | INTEGER_LITERAL | FLOATING_POINT_LITERAL | STRING_LITERAL | SYMBOL_LITERAL | TRUE | FALSE | NULL ⇒
literal()
case LPAREN ⇒
makeParens(noSeq.patterns)
case XML_START_OPEN | XML_COMMENT | XML_CDATA | XML_UNPARSED | XML_PROCESSING_INSTRUCTION ⇒
xmlLiteralPattern()
case _ ⇒
throw new ScalaParserException("illegal start of simple pattern: " + currentToken)
}
}
}
object outPattern extends PatternContextSensitive {
def argType() = typ()
def functionArgType() = paramType()
}
object seqOK extends SeqContextSensitive {
def interceptStarPattern() { if (STAR) nextToken() }
}
object noSeq extends SeqContextSensitive {
def interceptStarPattern() {}
}
def typ() = outPattern.typ()
def startInfixType() = outPattern.infixType()
def startAnnotType() = outPattern.annotType()
def exprTypeArgs() = outPattern.typeArgs()
def exprSimpleType() = outPattern.simpleType()
def pattern() = noSeq.pattern()
def patterns() = noSeq.patterns()
def seqPatterns() = seqOK.patterns()
private def argumentPatterns() = {
inParens { if (RPAREN) Nil else seqPatterns() }
}
private def accessQualifierOpt() =
if (LBRACKET) {
nextToken()
if (THIS)
nextToken()
else
ident()
accept(RBRACKET)
}
private def accessModifierOpt() = {
currentTokenType match {
case PRIVATE | PROTECTED ⇒
nextToken()
accessQualifierOpt()
case _ ⇒
None
}
}
private def modifiers() {
def loop() {
currentTokenType match {
case PRIVATE | PROTECTED ⇒
nextToken()
accessQualifierOpt()
loop()
case ABSTRACT | FINAL | SEALED | OVERRIDE | IMPLICIT | LAZY ⇒
nextToken()
loop()
case NEWLINE ⇒
nextToken()
loop()
case _ ⇒
}
}
loop()
}
private def localModifiers() {
if (isLocalModifier) {
nextToken()
localModifiers()
} else Nil
}
private def annotations(skipNewLines: Boolean) {
readAnnots {
annotationExpr()
if (skipNewLines) newLineOpt() else None
}
}
private def constructorAnnotations() =
readAnnots {
exprSimpleType()
argumentExprs()
}
private def annotationExpr() {
exprSimpleType()
if (LPAREN) multipleArgumentExprs()
}
private def paramClauses() {
var implicitmod = false
def param() {
annotations(skipNewLines = false)
val ownerIsTypeName = true // TODO: if (owner.isTypeName)
if (ownerIsTypeName) {
currentTokenType match {
case VAL | VAR ⇒ nextToken()
case _ ⇒
}
}
ident()
if (COLON || !forgiving) {
accept(COLON)
paramType()
if (EQUALS) {
nextToken()
expr()
}
}
}
// Differs from nsc in that we've pulled in lparen/rparen
def paramClause() {
accept(LPAREN)
if (RPAREN) {
accept(RPAREN)
} else {
if (IMPLICIT) {
nextToken()
implicitmod = true
}
commaSeparated(param())
accept(RPAREN)
}
}
newLineOptWhenFollowedBy(LPAREN)
while (!implicitmod && LPAREN) {
paramClause()
newLineOptWhenFollowedBy(LPAREN)
}
}
private def paramType() = {
currentTokenType match {
case ARROW ⇒
nextToken()
typ()
case _ ⇒
typ()
if (STAR)
nextToken()
}
}
private def typeParamClauseOpt(allowVariance: Boolean) {
def typeParam() {
if (allowVariance && isIdent) { // TODO: condition
if (PLUS)
nextToken()
else if (MINUS)
nextToken()
}
wildcardOrIdent()
typeParamClauseOpt(allowVariance = true)
typeBounds()
while (VIEWBOUND) {
nextToken()
typ()
}
while (COLON) {
nextToken()
typ()
}
}
newLineOptWhenFollowedBy(LBRACKET)
if (LBRACKET) {
inBrackets(commaSeparated((annotations(skipNewLines = true), typeParam())))
}
}
private def typeBounds() {
bound(SUPERTYPE)
bound(SUBTYPE)
}
private def bound(tokenType: TokenType) {
if (tokenType) {
nextToken()
typ()
}
}
private def importClause() {
accept(IMPORT)
commaSeparated(importExpr())
}
private def importExpr() {
def thisDotted() {
nextToken()
accept(DOT)
selector()
accept(DOT)
}
currentTokenType match {
case THIS ⇒
thisDotted()
case _ ⇒
ident()
accept(DOT)
if (THIS) thisDotted()
}
def loop() {
currentTokenType match {
case USCORE ⇒
nextToken()
case LBRACE ⇒
importSelectors()
case _ ⇒
ident()
if (DOT) {
nextToken()
loop()
}
}
}
loop()
}
private def importSelectors() {
inBraces(commaSeparated(importSelector()))
}
private def wildcardOrIdent() =
if (USCORE) nextToken()
else ident()
private def importSelector() {
wildcardOrIdent()
currentTokenType match {
case ARROW ⇒
nextToken()
wildcardOrIdent()
case _ ⇒
}
}
private def defOrDcl(localDef: Boolean = false) = currentTokenType match {
case VAL ⇒ patDefOrDcl()
case VAR ⇒ patDefOrDcl()
case DEF ⇒ funDefOrDcl(localDef)
case TYPE ⇒ typeDefOrDcl()
case _ ⇒ tmplDef()
}
def nonLocalDefOrDcl() {
annotations(skipNewLines = true)
modifiers()
defOrDcl()
}
private def patDefOrDcl() {
nextToken()
commaSeparated(noSeq.pattern2())
typedOpt()
if (EQUALS) { // TODO: Check cond
accept(EQUALS)
// Skip USCORE check: will be handled by expr() anyway
// if (USCORE) { // TODO: check cond
// nextToken()
// } else
expr()
}
}
private def funDefOrDcl(localDef: Boolean) {
accept(DEF)
if (THIS) {
nextToken()
paramClauses()
newLineOptWhenFollowedBy(LBRACE)
currentTokenType match {
case LBRACE ⇒
constrBlock()
case _ ⇒
accept(EQUALS)
constrExpr()
}
} else {
ident()
typeParamClauseOpt(allowVariance = false)
paramClauses()
newLineOptWhenFollowedBy(LBRACE)
typedOpt()
if (isStatSep || RBRACE || EOF /* for our tests */ )
None
else if (LBRACE) { // TODO: check cond
blockExpr()
} else {
equalsExpr()
}
}
}
private def constrExpr() {
if (LBRACE)
constrBlock()
else
selfInvocation()
}
private def selfInvocation() {
accept(THIS)
newLineOptWhenFollowedBy(LBRACE)
argumentExprs()
newLineOptWhenFollowedBy(LBRACE)
while (LPAREN || LBRACE) {
argumentExprs()
newLineOptWhenFollowedBy(LBRACE)
}
}
private def constrBlock() {
accept(LBRACE)
selfInvocation()
if (isStatSep) {
acceptStatSep() // <-- for inferred semi // nextToken()
blockStatSeq()
}
accept(RBRACE)
}
private def typeDefOrDcl() {
accept(TYPE)
newLinesOpt()
ident()
typeParamClauseOpt(allowVariance = true)
currentTokenType match {
case EQUALS ⇒
nextToken()
typ()
case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | EOF /* <-- for Scalariform tests */ ⇒
typeBounds()
case _ ⇒
throw new ScalaParserException("`=', `>:', or `<:' expected, but got " + currentToken)
}
}
private def topLevelTmplDef() {
annotations(skipNewLines = true)
modifiers()
tmplDef()
}
private def tmplDef() {
currentTokenType match {
case TRAIT ⇒ classDef()
case CLASS ⇒ classDef()
case CASE if lookahead(1) == CLASS ⇒ classDef()
case OBJECT ⇒ objectDef()
case CASE if lookahead(1) == OBJECT ⇒ objectDef()
case _ ⇒ throw new ScalaParserException("expected start of definition, but was " + currentTokenType)
}
}
private def classDef() {
if (CASE)
nextToken() // We use two tokens whereas nsc uses CASEOBJECT
val isTrait: Boolean = TRAIT
nextToken()
ident()
typeParamClauseOpt(allowVariance = true)
constructorAnnotations()
if (isTrait)
(None, None)
else {
accessModifierOpt()
paramClauses()
}
templateOpt(isTrait)
}
private def objectDef() {
if (CASE)
nextToken() // We use two tokens whereas nsc uses CASEOBJECT
accept(OBJECT)
ident()
templateOpt(isTrait = false)
}
private def templateParents(isTrait: Boolean) = {
startAnnotType()
if (LPAREN && !isTrait) multipleArgumentExprs()
else Nil
while (WITH) {
nextToken()
startAnnotType()
}
}
private def template(isTrait: Boolean) {
newLineOptWhenFollowedBy(LBRACE)
if (LBRACE) {
templateBody()
if (WITH) { // TODO check cond
nextToken()
templateParents(isTrait)
templateBodyOpt()
}
} else {
templateParents(isTrait)
templateBodyOpt()
}
}
private def templateOpt(isTrait: Boolean) {
if (EXTENDS || SUBTYPE && isTrait) {
nextToken()
template(isTrait)
} else {
// val newLineOpt = newLineOptWhenFollowedBy(LBRACE) // Will be picked up by templateBodyOpt ... TODO: double check this
templateBodyOpt()
}
}
private def templateBody() {
inBraces(templateStatSeq())
}
private def templateBodyOpt() {
newLineOptWhenFollowedBy(LBRACE)
if (LBRACE)
templateBody()
else if (LPAREN)
throw new ScalaParserException("traits or objects may not have parametsrs")
else
None
}
private def refinement() {
inBraces(refineStatSeq())
}
private def packaging() {
pkgQualId()
inBraces(topStatSeq())
}
private def topStatSeq() {
while (!isStatSeqEnd) {
currentTokenType match {
case PACKAGE ⇒
nextToken()
if (OBJECT) {
objectDef()
} else
packaging()
case IMPORT ⇒
importClause()
case x if x == AT || isTemplateIntro || isModifier ⇒
topLevelTmplDef()
case _ ⇒
if (!isStatSep)
throw new ScalaParserException("expected class or object definition")
else
None
}
val statSepOpt = if (!RBRACE && !EOF) acceptStatSep() else None
}
}
private def templateStatSeq() {
if (isExprIntro) {
expr(InTemplate)
if (ARROW) {
nextToken()
} else {
acceptStatSepOpt()
}
} else
None
while (!isStatSeqEnd) {
if (IMPORT)
Some(importClause())
else if (isExprIntro)
Some(statement(InTemplate))
else if (isDefIntro || isModifier || AT)
Some(nonLocalDefOrDcl())
else if (!isStatSep)
throw new ScalaParserException("illegal start of definition: " + currentToken)
else
None
acceptStatSepOpt()
}
}
private def refineStatSeq() {
while (!isStatSeqEnd) {
if (isDclIntro) {
defOrDcl()
} else if (!isStatSep)
throw new ScalaParserException("illegal start of definition: " + currentToken)
else
None
val statSepOpt = if (!RBRACE) acceptStatSep() else None
}
}
private def localDef() {
annotations(skipNewLines = true)
localModifiers()
// val modifierCondition = true // TODO: !!!!
or(defOrDcl(localDef = true), tmplDef())
}
private def blockStatSeq() {
while (!isStatSeqEnd && !justCase) {
if (IMPORT) {
importClause()
acceptStatSep()
} else if (isExprIntro) {
statement(InBlock)
if (!RBRACE && !justCase) Some(acceptStatSep()) else None
} else if (isDefIntro || isLocalModifier || AT) {
if (IMPLICIT) {
nextToken()
if (isIdent)
implicitClosure(InBlock)
else {
localDef()
}
} else
localDef()
acceptStatSepOpt()
} else if (isStatSep) {
acceptStatSep() // <-- for inferred semi // nextToken()
} else
throw new ScalaParserException("illegal start of statement: " + currentToken)
}
}
def compilationUnit() {
def topstats() {
while (SEMI)
nextToken()
if (PACKAGE) {
nextToken()
if (OBJECT) {
objectDef()
if (EOF)
()
else {
acceptStatSep()
topStatSeq()
}
} else {
pkgQualId()
if (EOF)
()
else if (isStatSep) {
acceptStatSep() // <-- to record inferred semi // nextToken()
topstats()
} else {
inBraces(topStatSeq())
topStatSeq()
}
}
} else
topStatSeq()
}
topstats()
accept(EOF)
}
private def xmlStartTag(isPattern: Boolean) {
accept(XML_START_OPEN)
accept(XML_NAME)
while (!XML_TAG_CLOSE) {
nextTokenIf(XML_WHITESPACE)
currentTokenType match {
case XML_NAME ⇒
xmlAttribute(isPattern)
case XML_TAG_CLOSE ⇒
// End loop
case _ ⇒
throw new ScalaParserException("Expected XML attribute or end of tag: " + currentToken)
}
}
accept(XML_TAG_CLOSE)
}
private def xmlAttribute(isPattern: Boolean) {
accept(XML_NAME)
nextTokenIf(XML_WHITESPACE)
accept(XML_ATTR_EQ)
nextTokenIf(XML_WHITESPACE)
currentTokenType match {
case XML_ATTR_VALUE ⇒
nextToken()
case LBRACE ⇒
xmlEmbeddedScala(isPattern)
}
}
private def xmlEmptyElement(isPattern: Boolean) {
accept(XML_START_OPEN)
accept(XML_NAME)
while (!XML_EMPTY_CLOSE) {
nextTokenIf(XML_WHITESPACE)
currentTokenType match {
case XML_NAME ⇒
xmlAttribute(isPattern)
case XML_EMPTY_CLOSE ⇒
// End loop
case _ ⇒
throw new ScalaParserException("Expected XML attribute or end of tag: " + currentToken)
}
}
accept(XML_EMPTY_CLOSE)
}
private def xmlEmbeddedScala(isPattern: Boolean) {
if (isPattern) {
accept(LBRACE)
seqPatterns()
accept(RBRACE)
} else
blockExpr()
}
private def xmlEndTag() {
accept(XML_END_OPEN)
accept(XML_NAME)
nextTokenIf(XML_WHITESPACE)
accept(XML_TAG_CLOSE)
}
private def xmlNonEmptyElement(isPattern: Boolean) {
xmlStartTag(isPattern)
while (!XML_END_OPEN) {
currentTokenType match {
case XML_START_OPEN ⇒ xmlElement(isPattern)
case XML_PCDATA ⇒ XmlPCDATA(nextToken())
case XML_COMMENT ⇒ XmlComment(nextToken())
case XML_CDATA ⇒ XmlCDATA(nextToken())
case XML_UNPARSED ⇒ XmlUnparsed(nextToken())
case XML_PROCESSING_INSTRUCTION ⇒ XmlProcessingInstruction(nextToken())
case LBRACE ⇒ xmlEmbeddedScala(isPattern)
case _ ⇒ throw new ScalaParserException("Unexpected token in XML: " + currentToken)
}
}
xmlEndTag()
}
private def xmlElement(isPattern: Boolean) {
or(xmlNonEmptyElement(isPattern), xmlEmptyElement(isPattern))
}
private def xml(isPattern: Boolean) {
def xmlContent() {
currentTokenType match {
case XML_START_OPEN ⇒ xmlElement(isPattern)
case XML_PCDATA ⇒ XmlPCDATA(nextToken())
case XML_COMMENT ⇒ XmlComment(nextToken())
case XML_CDATA ⇒ XmlCDATA(nextToken())
case XML_UNPARSED ⇒ XmlUnparsed(nextToken())
case XML_PROCESSING_INSTRUCTION ⇒ XmlProcessingInstruction(nextToken())
case _ ⇒ throw new ScalaParserException("Expected XML: " + currentToken)
}
xmlContent()
}
while (XML_START_OPEN || XML_PCDATA) {
if (XML_START_OPEN)
xmlElement(isPattern)
else
XmlPCDATA(accept(XML_PCDATA))
}
}
private def xmlLiteral() = xml(isPattern = false)
private def xmlLiteralPattern() = xml(isPattern = true)
private var tokensArray: Array[Token] = tokens.toArray
private var pos = 0
private def currentToken: Token = this(pos)
private def apply(pos: Int): Token =
if (pos < tokensArray.length)
tokensArray(pos)
else
tokens.last
private def currentTokenType = currentToken.tokenType
/** @return the token before advancing */
private def nextToken(): Token = {
val token = currentToken
pos += 1
if (logging)
println("nextToken(): " + token + " --> " + currentToken)
token
}
private def lookahead(n: Int): TokenType = this(pos + n).tokenType
private implicit def tokenType2Boolean(tokenType: TokenType): Boolean = currentTokenType == tokenType
private def caseClass = CASE && lookahead(1) == CLASS
private def caseObject = CASE && lookahead(1) == OBJECT
private def justCase = CASE && lookahead(1) != CLASS && lookahead(1) != OBJECT
private abstract sealed class Location
private case object Local extends Location
private case object InBlock extends Location
private case object InTemplate extends Location
private def isLeftAssoc(token: Token) =
token.getText.size > 0 && token.getText.last != ':'
private def isVariableName(name: String): Boolean = {
val first = name(0)
((first.isLower && first.isLetter) || first == '_')
}
private def isVarPattern(token: Token) = {
isIdent(token.tokenType) &&
isVariableName(token.getText) &&
!token.getText.startsWith("`")
}
private def optional[T](p: ⇒ T): Option[T] =
or(Some(p), None)
private def or[T](p1: ⇒ T, p2: ⇒ T): T = {
val originalPos = pos
try {
p1
} catch {
case e: ScalaParserException ⇒
pos = originalPos
if (logging) println("Rewinding to try alternative: " + currentToken)
p2
}
}
private def nextTokenIf(tokenType: TokenType): Option[Token] = {
if (tokenType) {
val token = currentToken
nextToken()
Some(token)
} else
None
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy