From 64c81890a5ac1ef27db7e7a44de4239365441341 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Dec 2006 11:35:38 +0000 Subject: added infix types and {...} syntax for tuples. changed a confusing error message "value this is not a member of T". --- .../scala/tools/nsc/ast/parser/MarkupParsers.scala | 2 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 166 ++++++++++++++------- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 8 +- .../scala/tools/nsc/symtab/Definitions.scala | 2 +- src/compiler/scala/tools/nsc/symtab/Types.scala | 4 +- .../scala/tools/nsc/transform/ExplicitOuter.scala | 6 +- .../scala/tools/nsc/typechecker/Typers.scala | 14 +- 7 files changed, 133 insertions(+), 69 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala index 3db2bd7673..4e1d73b53f 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala @@ -575,7 +575,7 @@ class MarkupParser(unit: CompilationUnit, s: Scanner, p: Parser, presWS: boolean */ def xScalaPatterns: List[Tree] = { sync; - val b = p.patterns(); + val b = p.patterns(true); if (/*s.*/token != RBRACE) { reportSyntaxError(" expected end of Scala patterns"); } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index f47ced301d..9fd03dedc0 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -300,6 +300,10 @@ trait Parsers requires SyntaxAnalyzer { } } + def checkSize(kind: String, size: int, max: int) { + if (size > max) syntaxError("too many "+kind+", maximum = "+max, false) + } + def checkAssoc(pos: int, op: Name, leftAssoc: boolean) = if (treeInfo.isLeftAssoc(op) != leftAssoc) syntaxError( @@ -549,9 +553,10 @@ trait Parsers requires SyntaxAnalyzer { val t0 = typ() if (in.token == COMMA) { in.nextToken() - val ts = new ListBuffer[Tree] + t0 ++ types() + val ts = t0 :: types() accept(RPAREN) - atPos (accept(ARROW)) { makeFunctionTypeTree(ts.toList, typ()) } + checkSize("function arguments", ts.length, definitions.MaxFunctionArity) + atPos (accept(ARROW)) { makeFunctionTypeTree(ts, typ()) } } else { accept(RPAREN) infixTypeRest(pos, t0, false, FirstOp) @@ -574,7 +579,7 @@ trait Parsers requires SyntaxAnalyzer { def infixTypeRest(pos: int, t0: Tree, isPattern: boolean, mode: int): Tree = { val t = compoundTypeRest(pos, t0, isPattern) if (isIdent && in.name != nme.STAR) { - val opPos = in.pos + val opPos = in.currentPos val leftAssoc = treeInfo.isLeftAssoc(in.name) if (mode == LeftOp) checkAssoc(opPos, in.name, true) else if (mode == RightOp) checkAssoc(opPos, in.name, false) @@ -582,7 +587,7 @@ trait Parsers requires SyntaxAnalyzer { newLineOptWhenFollowing(isTypeIntroToken) def mkOp(t1: Tree) = atPos(opPos) { AppliedTypeTree(Ident(op.toTypeName), List(t, t1)) } if (leftAssoc) - infixTypeRest(in.pos, mkOp(compoundType(isPattern)), isPattern, LeftOp) + infixTypeRest(in.currentPos, mkOp(compoundType(isPattern)), isPattern, LeftOp) else mkOp(infixType(isPattern, RightOp)) } else t @@ -610,11 +615,13 @@ trait Parsers requires SyntaxAnalyzer { * | StableId * | Path `.' type * | `(' Type `)' + * | `{' [Type `,' Types] `}' * | AttributeClauses SimpleType // if Xplugtypes enabled - * SimpleTypePattern ::= SimpleTypePattern TypePatternArgs + * SimpleTypePattern ::= SimpleTypePattern1 [TypePatternArgs] * SimpleTypePattern1 ::= SimpleTypePattern1 "#" Id * | StableId * | Path `.' type + * | `{' [ArgTypePattern `,' ArgTypePatterns] `}' */ def simpleType(isPattern: boolean): Tree = { if(settings.Xplugtypes.value) @@ -627,6 +634,17 @@ trait Parsers requires SyntaxAnalyzer { val t = typ() accept(RPAREN) t + } else if (in.token == LBRACE) { + in.nextToken() + val ts = if (in.token == RBRACE) List() + else { + val t1 = argType(isPattern) + accept(COMMA) + t1 :: argTypes(isPattern) + } + checkSize("tuple elements", ts.length, definitions.MaxTupleArity) + accept(RBRACE) + makeTupleType(ts) } else { val r = path(false, true) val x = r match { @@ -637,38 +655,47 @@ trait Parsers requires SyntaxAnalyzer { x } while (true) { - if (in.token == HASH) + if (in.token == HASH) { t = atPos(in.skipToken()) { SelectFromTypeTree(t, ident().toTypeName) } - else if (in.token == LBRACKET) + } else if (in.token == LBRACKET) { t = atPos(pos) { AppliedTypeTree(t, typeArgs(isPattern)) } - else + if (isPattern) return t + } else return t } null; //dummy } - /** TypeArgs ::= `[' TypeArg {`,' TypeArg} `]' - * TypePatternArgs ::= '[' TypePatternArg {`,' TypePatternArg} `]' + /** TypeArgs ::= `[' ArgTypes `]' + * TypePatternArgs ::= '[' ArgTypePatterns `]' */ def typeArgs(isPattern: boolean): List[Tree] = { accept(LBRACKET) - val ts = new ListBuffer[Tree] + typeArg(isPattern) + val ts = argTypes(isPattern) + accept(RBRACKET) + ts + } + + /** ArgTypes ::= ArgType {`,' ArgType} + * ArgTypePatterns ::= ArgTypePattern {`,' ArgTypePattern} + */ + def argTypes(isPattern: boolean): List[Tree] = { + val ts = new ListBuffer[Tree] + argType(isPattern) while (in.token == COMMA) { in.nextToken() - ts += typeArg(isPattern) + ts += argType(isPattern) } - accept(RBRACKET) ts.toList } - /** TypeArg ::= Type - * TypePatternArg ::= varid + /** ArgType ::= Type + * ArgTypePattern ::= varid * | `_' * | Type // for array elements only! */ - def typeArg(isPattern: boolean): Tree = + def argType(isPattern: boolean): Tree = if (isPattern) { if (in.token == USCORE) atPos(in.skipToken()) { Bind(nme.WILDCARD.toTypeName, EmptyTree) } @@ -736,6 +763,7 @@ trait Parsers requires SyntaxAnalyzer { final val IsArgument = 1 final val IsInBlock = 2 + final val ClosureOK = 4 /** Expr ::= (Bindings | Id) `=>' Expr * | Expr1 @@ -758,20 +786,22 @@ trait Parsers requires SyntaxAnalyzer { * Binding ::= Id [`:' Type] */ def expr(): Tree = - liftingScope(exprImpl(0)) + liftingScope(exprImpl(ClosureOK)) def blockStatExpr(): Tree = { - liftingScope(exprImpl(IsInBlock)) + liftingScope(exprImpl(IsInBlock | ClosureOK)) } def argExpr(): Tree = { - exprImpl(IsArgument) + exprImpl(IsArgument | ClosureOK) } def localExpr(): Tree = { - exprImpl(0) + exprImpl(ClosureOK) } + def expr1(): Tree = exprImpl(0) + private def exprImpl(mode: int): Tree = in.token match { case IF => val pos = in.skipToken() @@ -846,7 +876,7 @@ trait Parsers requires SyntaxAnalyzer { liftingScope(makeClosure(simpleExpr())) // Note: makeClosure does some special treatment of liftedGenerators } else { - syntaxError("identifier expected", true); + syntaxError("identifier expected", true) errorTermTree } } @@ -873,18 +903,10 @@ trait Parsers requires SyntaxAnalyzer { syntaxError(in.currentPos, "`*' expected", true) } } else { - t = atPos(pos) { Typed(t, if ((mode & IsInBlock) != 0) compoundType(false) else typ()) } - if ((mode & IsInBlock) != 0 && in.token == COMMA) { - val vdefs = new ListBuffer[ValDef] - while (in.token == COMMA) { - in.nextToken() - vdefs += ValDef(Modifiers(Flags.PARAM), ident(), typedOpt(), EmptyTree) - } - if (in.token == ARROW) { - t = atPos(in.skipToken()) { - Function(convertToParams(t) ::: vdefs.toList, block()) - } - } else syntaxError(in.currentPos, "`=>' expected", true) + t = atPos(pos) { + Typed(t, if ((mode & IsInBlock) != 0) compoundType(false) else typ()) + // this does not correspond to syntax, but is necessary to + // accept closures. We should restrict closures to be between {...} only! } } } else if (in.token == MATCH) { @@ -895,7 +917,7 @@ trait Parsers requires SyntaxAnalyzer { Match(t, cases): Tree } } - if (in.token == ARROW) { + if ((mode & ClosureOK) != 0 && in.token == ARROW) { t = atPos(in.skipToken()) { Function(convertToParams(t), if ((mode & IsInBlock) != 0) block() else expr()) } @@ -1064,20 +1086,23 @@ trait Parsers requires SyntaxAnalyzer { } } - /** BlockExpr ::= `{' CaseClauses | Block `}' + /** BlockExpr ::= `{' CaseClauses | Block | Tuple `}' */ def blockExpr(): Tree = { val res = atPos(accept(LBRACE)) { if (in.token == CASE) makeVisitor(caseClauses()) - else block() + else blockOrTuple(true) } accept(RBRACE) res } /** Block ::= BlockStatSeq - */ - def block(): Tree = makeBlock(blockStatSeq(new ListBuffer[Tree])) + * Tuple ::= [Expr1 {`,' Expr1}] + */ + def block(): Tree = blockOrTuple(false) + def blockOrTuple(tupleOK: boolean): Tree = + makeBlock(blockStatSeqOrTuple(tupleOK, new ListBuffer[Tree])) /** CaseClauses ::= CaseClause {CaseClause} */ @@ -1126,12 +1151,13 @@ trait Parsers requires SyntaxAnalyzer { //////// PATTERNS //////////////////////////////////////////////////////////// - /** Patterns ::= SeqPattern { , SeqPattern } */ - def patterns(): List[Tree] = { + /** Patterns ::= Pattern { `,' Pattern } */ + /** SeqPatterns ::= SeqPattern { `,' SeqPattern } */ + def patterns(seqOK: boolean): List[Tree] = { val ts = new ListBuffer[Tree] - ts += pattern(true) + ts += pattern(seqOK) while (in.token == COMMA) { - in.nextToken(); ts += pattern(true) + in.nextToken(); ts += pattern(seqOK) } ts.toList } @@ -1227,14 +1253,16 @@ trait Parsers requires SyntaxAnalyzer { * | `_' * | literal * | XmlPattern - * | StableId [ `(' Patterns `)' ] + * | StableId [ `(' SeqPatterns `)' ] * | `(' [Pattern] `)' + * | `{' [Pattern `,' Patterns] `}' * SimpleSeqPattern ::= varid * | `_' * | literal * | `<' xLiteralPattern - * | StableId [TypePatternArgs] `(' Patterns `)' ] - * | `(' Patterns `)' + * | StableId [TypePatternArgs] `(' SeqPatterns `)' ] + * | `{' [Pattern `,' Patterns] `}' + * | `(' SeqPatterns `)' */ def simplePattern(seqOK: boolean): Tree = in.token match { case IDENTIFIER | BACKQUOTED_IDENT | THIS => @@ -1253,17 +1281,28 @@ trait Parsers requires SyntaxAnalyzer { atPos(in.currentPos) { val ts = typeArgs(true) accept(LPAREN) - val ps = if (in.token == RPAREN) List() else patterns() + val ps = if (in.token == RPAREN) List() else patterns(true) accept(RPAREN) Apply(TypeApply(convertToTypeId(t), ts), ps) } else */ if (in.token == LPAREN) { atPos(in.skipToken()) { - val ps = if (in.token == RPAREN) List() else patterns() + val ps = if (in.token == RPAREN) List() else patterns(true) accept(RPAREN) Apply(convertToTypeId(t), ps) } + } else if (in.token == LBRACE) { + in.nextToken() + val ts = if (in.token == RBRACE) List() + else { + val p1 = pattern() + accept(COMMA) + p1 :: patterns(false) + } + checkSize("tuple elements", ts.length, definitions.MaxTupleArity) + accept(RBRACE) + makeTupleTerm(ts) } else t case USCORE => atPos(in.skipToken()) { Ident(nme.WILDCARD) } @@ -1272,7 +1311,7 @@ trait Parsers requires SyntaxAnalyzer { case LPAREN => val pos = in.skipToken() val p = - //if (false /*disabled, no regexp matching*/ && seqOK) atPos(pos) { makeSequence(patterns()) } + //if (false /*disabled, no regexp matching*/ && seqOK) atPos(pos) { makeSequence(patterns(true)) } //else if (in.token != RPAREN) pattern(false) else Literal(()).setPos(pos) @@ -1864,11 +1903,8 @@ trait Parsers requires SyntaxAnalyzer { if (mods.hasFlag(Flags.CASE)) { if (!vparamss.isEmpty) { val argtypes: List[Tree] = vparamss.head map (.tpt.duplicate) //remove type annotation and you will get an interesting error message!!! - val nargs = argtypes.length - if (nargs <= definitions.MaxProductArity) - parents += productConstr(argtypes) - else - unit.warning(in.currentPos, "can't have more than "+definitions.MaxProductArity+" case fields ") + checkSize("case class parameters", argtypes.length, definitions.MaxProductArity) + if (argtypes.length <= definitions.MaxProductArity) parents += productConstr(argtypes) } else { parents += productConstr(Nil) } @@ -2067,8 +2103,11 @@ trait Parsers requires SyntaxAnalyzer { * | LocalModifiers TmplDef * | Expr1 * | + * TupleExprs ::= ResultExpr "," ResultExprs */ - def blockStatSeq(stats: ListBuffer[Tree]): List[Tree] = { + def blockStatSeq(stats: ListBuffer[Tree]): List[Tree] = + blockStatSeqOrTuple(false, stats) + def blockStatSeqOrTuple(tupleOK: boolean, stats: ListBuffer[Tree]): List[Tree] = { def localDef(mods: Modifiers) = { if (!(mods hasFlag ~Flags.IMPLICIT)) stats ++= defOrDcl(mods) else stats += tmplDef(mods) @@ -2079,12 +2118,27 @@ trait Parsers requires SyntaxAnalyzer { if (in.token == RBRACE || in.token == CASE) stats += Literal(()).setPos(in.currentPos) } - while ((in.token != RBRACE) && (in.token != EOF) && (in.token != CASE)) { + var last = false + while ((in.token != RBRACE) && (in.token != EOF) && (in.token != CASE) && !last) { if (in.token == IMPORT) { stats ++= importClause() acceptStatSep() } else if (isExprIntro) { - stats += blockStatExpr() + val expr = blockStatExpr() + if (in.token == COMMA) { + val exprs = new ListBuffer[Tree] + expr + while (in.token == COMMA) { + in.nextToken() + exprs += expr1() + } + if (in.token == ARROW) { + val vdefs = exprs.toList flatMap convertToParams + checkSize("function arguments", vdefs.length, definitions.MaxFunctionArity) + stats += atPos(in.skipToken()) { Function(vdefs, block()) } + } else { + stats += makeTupleTerm(exprs.toList) + } + } else stats += expr if (in.token != RBRACE && in.token != CASE) acceptStatSep() } else if (isDefIntro) { localDef(NoMods) diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index e98793f736..4060d8f619 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -94,12 +94,18 @@ abstract class TreeBuilder { Apply(scalaDot(if (isType) newTypeName(tupString) else newTermName(tupString)), trees) } - private def makeTupleTerm(trees: List[Tree]): Tree = trees match { + def makeTupleTerm(trees: List[Tree]): Tree = trees match { case List() => Literal(()) case List(tree) => tree case _ => makeTuple(trees, false) } + def makeTupleType(trees: List[Tree]): Tree = trees match { + case List() => scalaUnitConstr + case List(tree) => tree + case _ => AppliedTypeTree(scalaDot(newTypeName("Tuple" + trees.length)), trees) + } + /** If tree is a variable pattern, return Some("its name and type"). * Otherwise return none */ private def matchVarPattern(tree: Tree): Option[Pair[Name, Tree]] = tree match { diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 78354d92f2..809da82ee5 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -98,7 +98,7 @@ trait Definitions requires SymbolTable { var RepeatedParamClass: Symbol = _ var ByNameParamClass: Symbol = _ - val MaxTupleArity = 9 + val MaxTupleArity = 9 //todo: lift to 22 val TupleClass: Array[Symbol] = new Array(MaxTupleArity + 1) def tupleField(n: Int, j: Int) = getMember(TupleClass(n), "_" + j) def isTupleType(tp: Type): Boolean = tp match { diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 6207e93ee0..5cc54503ab 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -1,4 +1,4 @@ - /* NSC -- new Scala compiler +/* NSC -- new Scala compiler * Copyright 2005-2006 LAMP/EPFL * @author Martin Odersky */ @@ -960,6 +960,8 @@ trait Types requires SymbolTable { return "=> " + args(0).toString() if (isFunctionType(this)) return args.init.mkString("(", ", ", ")") + " => " + args.last + if (isTupleType(this)) + return args.mkString("{", ", ", "}") } val str = (pre.prefixString + sym.nameString + (if (args.isEmpty) "" else args.mkString("[", ",", "]"))) diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 70996371b8..b205c38f55 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -391,7 +391,7 @@ abstract class ExplicitOuter extends InfoTransform with TransMatcher with Patter else Select(This(currentClass), outerField(currentClass)) localTyper.typed { atPos(currentClass.pos) { - DefDef(outerAcc, vparamss => rhs) + DefDef(outerAcc, {vparamss => rhs}) } } } @@ -413,7 +413,7 @@ abstract class ExplicitOuter extends InfoTransform with TransMatcher with Patter val rhs = ExplicitOuterTransformer.this.transform(path) localTyper.typed { atPos(currentClass.pos) { - DefDef(outerAcc, vparamss => rhs) + DefDef(outerAcc, {vparamss=>rhs}) } } } @@ -560,7 +560,7 @@ abstract class ExplicitOuter extends InfoTransform with TransMatcher with Patter if(settings.Xkilloption.value) { //Console.println("vparamss"+vparamss) - val nvparamss = vparamss.map { x => super.transformValDefs(x) /*x.map { y => transform(y) if(wasOptionRef(y.tpe)) y.setType(getOptionArg(y.tpe)) */} + val nvparamss = vparamss.map { x => super.transformValDefs(x) } //Console.println("nvparamss"+nvparamss) val ntpt = if(wasOptionRef(tpt.tpe)) TypeTree(getOptionArg(tpt.tpe)) else tpt diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 75a0229869..aa6f5148f8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1664,13 +1664,15 @@ trait Typers requires Analyzer { if (!sym.exists) { if (settings.debug.value) Console.err.println("qual = "+qual+":"+qual.tpe+"\nSymbol="+qual.tpe.symbol+"\nsymbol-info = "+qual.tpe.symbol.info+"\nscope-id = "+qual.tpe.symbol.info.decls.hashCode()+"\nmembers = "+qual.tpe.members+"\nname = "+name+"\nfound = "+sym+"\nowner = "+context.enclClass.owner) if (!qual.tpe.widen.isErroneous) { - if (false && (context.unit eq null)) assert(false, "("+qual+":"+qual.tpe+")."+name) error(tree.pos, - decode(name)+" is not a member of "+qual.tpe.widen + - (if ((context.unit ne null) && Position.line(context.unit.source, qual.pos) < - Position.line(context.unit.source, tree.pos)) - "\npossible cause: maybe a semicolon is missing before `"+decode(name)+"'?" - else "")) + if (name == nme.CONSTRUCTOR) + qual.tpe.widen+" does not have a constructor" + else + decode(name)+" is not a member of "+qual.tpe.widen + + (if ((context.unit ne null) && Position.line(context.unit.source, qual.pos) < + Position.line(context.unit.source, tree.pos)) + "\npossible cause: maybe a semicolon is missing before `"+decode(name)+"'?" + else "")) } setError(tree) } else { -- cgit v1.2.3