/* ____ ____ ____ ____ ______ *\ ** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** ** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** ** /_____/\____/\___/\____/____/ ** ** ** ** $Id$ \* */ package scalac.ast.parser; import ch.epfl.lamp.util.Position; import java.util.*; import scalac.*; import scalac.util.*; import scalac.symtab.Modifiers; import scalac.ast.*; import Tree.*; /** A recursive descent parser for the programming language Scala. * * @author Martin Odersky, Matthias Zenger, Burak Emir * @version 1.2 */ public class Parser implements Tokens { /** the lexical analyzer */ Scanner s; /** the tree factory */ TreeFactory make; /** pattern checker and normalizer */ PatternNormalizer pN; /** The current nesting depths of while and do loops. */ int loopNestingDepth; public Parser(Unit unit) { s = new Scanner(unit); make = unit.global.make; pN = new PatternNormalizer( unit ); mapTreeComment = unit.global.mapTreeComment; loopNestingDepth = 0; } /** this is the general parse method */ public Tree[] parse() { Tree[] ts = s.unit.console ? templateStatSeq() : compilationUnit(); accept(EOF); return ts; } /////// ERROR HANDLING ////////////////////////////////////////////////////// private void skip() { //System.out.println(" " + s.token2string(s.token));//DEBUG int nparens = 0; int nbraces = 0; while (true) { switch (s.token) { case EOF: return; case SEMI: if (nparens == 0 && nbraces == 0) return; break; case RPAREN: nparens--; break; case RBRACE: if (nbraces == 0) return; nbraces--; break; case LPAREN: nparens++; break; case LBRACE: nbraces++; break; } //System.out.println("skipped: " + s.token2string(s.token));//DEBUG s.nextToken(); } } Tree syntaxError(String msg, boolean skip) { return syntaxError(s.pos, msg, skip); } Tree syntaxError(int pos, String msg, boolean skip) { if (pos != s.errpos) { s.unit.error(pos, msg); s.errpos = pos; } if (skip) skip(); return make.Bad(pos); } int accept(int token) { int pos = s.pos; if (s.token != token) { int errpos = ((s.pos >>> Position.COLUMN_BITS) > (s.lastpos >>> Position.COLUMN_BITS)) ? s.lastpos : s.pos; syntaxError(errpos, s.token2string(token) + " expected but " + s.token2string(s.token) + " found.", true); } if (s.token == token) s.nextToken(); return pos; } /////// TOKEN CLASSES ////////////////////////////////////////////////////// boolean isModifier() { return (s.token == ABSTRACT) || (s.token == FINAL) || (s.token == SEALED) || (s.token == PRIVATE) || (s.token == PROTECTED) || (s.token == OVERRIDE); } boolean isLocalModifier() { return (s.token == ABSTRACT) || (s.token == FINAL) || (s.token == SEALED); } boolean isDefIntro() { switch (s.token) { case VAL: case VAR: case DEF: case TYPE: case OBJECT: case CASEOBJECT: case CLASS: case CASECLASS: case TRAIT: return true; default: return false; } } boolean isDclIntro() { switch (s.token) { case VAL: case VAR: case DEF: case TYPE: return true; default: return false; } } boolean isExprIntro() { switch (s.token) { case CHARLIT: case INTLIT: case LONGLIT: case FLOATLIT: case DOUBLELIT: case STRINGLIT: case SYMBOLLIT: case TRUE: case FALSE: case NULL: case IDENTIFIER: case THIS: case SUPER: case IF: case FOR: case NEW: case USCORE: case TRY: case WHILE: case DO: case RETURN: case THROW: case LPAREN: case LBRACE: return true; default: return false; } } /////// COMMENT COLLECTION /////////////////////////////////////////////////// /** keep the comments associated with a given tree */ protected Map mapTreeComment; /** stack of comments */ protected final Stack commentStack = new Stack(); /** positive if we are inside a block */ protected int local = 0; /** push last encountered comment and reset the buffer */ protected void pushComment() { if (local == 0) { commentStack.push(s.docBuffer == null ? null : s.docBuffer.toString()); s.docBuffer = null; } } /** pop a comment from the stack and associate it with the given tree */ protected Tree popComment(Tree tree) { if (local == 0) if (!commentStack.empty()) mapTreeComment.put(tree, (String) commentStack.pop()); return tree; } /////// TREE CONSTRUCTION //////////////////////////////////////////////////// /** Name supply */ int fresh = 0; Name fresh() { return Name.fromString("x$" + (fresh++)); } /** Create tree representing binary operation expression or pattern. */ Tree makeBinop(boolean isExpr, int pos, Tree left, Name op, Tree right) { if (isExpr) { if (op.isLeftAssoc()) { return make.Apply(pos, make.Select(pos, left, NameTransformer.encode(op)), new Tree[]{right}); } else { Name x = fresh(); return make.Block(pos, new Tree[]{ make.ValDef(pos, 0, x, Tree.Empty, left), make.Apply(pos, make.Select(pos, right, NameTransformer.encode(op)), new Tree[]{make.Ident(left.pos, x)})}); } } else { return make.Apply(pos, make.Ident(pos, NameTransformer.encode(op).toTypeName()), new Tree[]{left, right}); } } Tree scalaDot(int pos, Name name) { return make.Select(pos, make.Ident(pos, Names.scala), name); } Tree scalaRuntimeDot(int pos, Name name) { return make.Select(pos, scalaDot(pos, Names.runtime), name); } Tree ScalaRunTimeDot(int pos, Name name) { return make.Select(pos, scalaRuntimeDot(pos, Names.ScalaRunTime), name); } Tree scalaBooleanDot(int pos, Name name) { return make.Select(pos, scalaDot(pos, Names.Boolean), name); } Tree scalaObjectConstr(int pos) { return make.Apply( pos, scalaDot(pos, Names.Object.toTypeName()), Tree.EMPTY_ARRAY); } /** Create tree for for-comprehension or * where mapName and flatmapName are chosen * corresponding to whether this is a for-do or a for-yield. */ Tree makeFor(int pos, Tree[] enums, Name mapName, Name flatmapName, Tree body) { switch (enums[0]) { case PatDef(int mods, Tree pat, Tree rhs): if (enums.length == 1) return makeFor1(pos, mapName, pat, rhs, body); Tree[] newenums = new Tree[enums.length - 1]; switch (enums[1]) { case PatDef(int mods2, Tree pat2, Tree rhs2): System.arraycopy(enums, 1, newenums, 0, newenums.length); return makeFor1(pos, flatmapName, pat, rhs, makeFor(enums[1].pos, newenums, mapName, flatmapName, body)); default: System.arraycopy(enums, 2, newenums, 1, newenums.length - 1); newenums[0] = make.PatDef( enums[0].pos, mods, pat, makeFor1(enums[1].pos, Names.filter, pat.duplicate(), rhs, enums[1])); return makeFor(pos, newenums, mapName, flatmapName, body); } default: throw new ApplicationError(); } } //where Tree makeFor1(int pos, Name name, Tree pat, Tree rhs, Tree body) { return make.Apply( pos, make.Select(pos, rhs, name), new Tree[]{makeForCont(pos, pat, body)}); } Tree makeForCont(int pos, Tree pat, Tree body) { switch (pat) { case Ident(Name name1): if (name1.isVariable()) return make.Function( pos, new Tree.ValDef[]{ (ValDef) make.ValDef( pat.pos, Modifiers.PARAM, name1, Tree.Empty, Tree.Empty)}, body); } return make.Visitor(pos, new Tree.CaseDef[]{ (CaseDef)make.CaseDef(pos, pat, Tree.Empty, body)}); } Tree makeTry(int pos, Tree body, Tree catcher, Tree finalizer) { Tree t = body; if (catcher != Tree.Empty) t = make.Apply( pos, make.Select( pos, make.Apply( pos, ScalaRunTimeDot(pos, Names.Try), new Tree[]{t}), Names.Catch), new Tree[]{catcher}); if (finalizer != Tree.Empty) t = make.Apply( pos, make.Select( pos, make.Apply( pos, ScalaRunTimeDot(pos, Names.Try), new Tree[]{t}), Names.Finally), new Tree[]{finalizer}); return t; } Tree makeWhile(int pos, Name lname, Tree cond, Tree body) { Tree continu = make.Apply( pos, make.Ident(pos, lname), Tree.EMPTY_ARRAY); Tree rhs = make.If( pos, cond, make.Block(body.pos, new Tree[]{body, continu}), make.Block(pos, Tree.EMPTY_ARRAY)); return make.LabelDef(pos, lname, new Ident[0], rhs); } Tree makeDoWhile(int pos, Name lname, Tree body, Tree cond) { Tree continu = make.Apply( pos, make.Ident(pos, lname), Tree.EMPTY_ARRAY); Tree rhs = make.Block( body.pos, new Tree[]{ body, make.If( cond.pos, cond, continu, make.Block(pos, Tree.EMPTY_ARRAY))}); return make.LabelDef(pos, lname, new Ident[0], rhs); } /** Convert tree to formal parameter list */ ValDef[] convertToParams(Tree t) { switch (t) { case Function(ValDef[] params, Tree.Empty): return params; case Ident(_): case Typed(Ident(_), _): return new ValDef[]{convertToParam(t)}; case Block(Tree[] stats): if (stats.length == 0) return Tree.ValDef_EMPTY_ARRAY; } syntaxError(t.pos, "malformed formal parameter list", false); return Tree.ValDef_EMPTY_ARRAY; } /** Convert list of trees to formal parameter list */ ValDef[] convertToParams(Tree[] ts) { ValDef[] res = new ValDef[ts.length]; for (int i = 0; i < res.length; i++) res[i] = convertToParam(ts[i]); return res; } /** Convert tree to formal parameter */ ValDef convertToParam(Tree tree) { switch (tree) { case Ident(Name name): return (ValDef)make.ValDef( tree.pos, Modifiers.PARAM, name, Tree.Empty, Tree.Empty); case Typed(Ident(Name name), Tree tpe): return (ValDef)make.ValDef( tree.pos, Modifiers.PARAM, name, tpe, Tree.Empty); default: Tree tpe = syntaxError(tree.pos, "not a legal formal parameter", false); return (ValDef)make.ValDef( tree.pos, Modifiers.PARAM, Names.ERROR, tpe, Tree.Empty); } } /** Convert (qual)ident to type identifier */ Tree convertToTypeId(Tree t) { switch (t) { case Ident(Name name): return make.Ident(t.pos, name.toTypeName()); case Select(Tree qual, Name name): return make.Select(t.pos, qual, name.toTypeName()); default: return t; } } /** Convert (qual)ident to constructor identifier */ Tree convertToConstr(Tree t) { switch (t) { case Ident(Name name): return make.Ident(t.pos, name.toTypeName()); case Select(Tree qual, Name name): return make.Select(t.pos, qual, name.toTypeName()); default: return syntaxError(t.pos, "class constructor expected", false); } } /** Complete unapplied constructor with `()' arguments */ Tree applyConstr(Tree t) { switch (t) { case Apply(_, _): return t; default: return make.Apply(t.pos, t, Tree.EMPTY_ARRAY); } } /////// OPERAND/OPERATOR STACK ///////////////////////////////////////////////// Tree[] operands = new Tree[8]; int[] positions = new int[8]; Name[] operators = new Name[8]; int sp = 0; void push(Tree od, int pos, Name op) { if (sp == operands.length) { Tree[] operands1 = new Tree[sp * 2]; System.arraycopy(operands, 0, operands1, 0, sp); operands = operands1; int[] positions1 = new int[sp * 2]; System.arraycopy(positions, 0, positions1, 0, sp); positions = positions1; Name[] operators1 = new Name[sp * 2]; System.arraycopy(operators, 0, operators1, 0, sp); operators = operators1; } operands[sp] = od; positions[sp] = pos; operators[sp] = op; sp++; } Tree reduceStack(boolean isExpr, int base, Tree top, int prec, boolean leftAssoc) { if (sp != base && operators[sp-1].precedence() == prec && operators[sp-1].isLeftAssoc() != leftAssoc) { syntaxError( positions[sp-1], "left- and right-associative operators with same precedence may not be mixed", false); } while (sp != base && (prec < operators[sp-1].precedence() || (leftAssoc && prec == operators[sp-1].precedence()))) { sp--; top = makeBinop(isExpr, positions[sp], operands[sp], operators[sp], top); } return top; } /////// IDENTIFIERS AND LITERALS //////////////////////////////////////////////////////////// static final Name MINUS = Name.fromString("-"); static final Name PLUS = Name.fromString("+"); static final Name BANG = Name.fromString("!"); static final Name TILDE = Name.fromString("~"); static final Name STAR = Name.fromString("*"); static final Name BAR = Name.fromString("|"); static final Name OPT = Name.fromString("?"); Name ident() { if (s.token == IDENTIFIER) { Name name = NameTransformer.encode(s.name); s.nextToken(); return name; } else { accept(IDENTIFIER); return Names.ERROR; } } /** StableRef ::= StableId * | [Ident `.'] this * SimpleType ::= StableRef [`.' type] */ Tree stableRef(boolean thisOK, boolean typeOK) { Tree t; if (s.token == THIS) { t = make.This(s.skipToken(), TypeNames.EMPTY); if (!thisOK || s.token == DOT) t = selectors(accept(DOT), t, typeOK); } else if (s.token == SUPER) { t = make.Super( s.skipToken(), TypeNames.EMPTY, mixinQualifierOpt()); t = make.Select(accept(DOT), t, ident()); if (s.token == DOT) t = selectors(s.skipToken(), t, typeOK); } else { Ident i = make.Ident(s.pos, ident()); t = i; if (s.token == DOT) { int pos = s.skipToken(); if (s.token == THIS) { s.nextToken(); t = make.This(i.pos, i.name.toTypeName()); if (!thisOK || s.token == DOT) t = selectors(accept(DOT), t, typeOK); } else if (s.token == SUPER) { s.nextToken(); t = make.Super( i.pos, i.name.toTypeName(), mixinQualifierOpt()); t = make.Select(accept(DOT), t, ident()); if (s.token == DOT) t = selectors(s.skipToken(), t, typeOK); } else { t = selectors(pos, t, typeOK); } } } return t; } Tree selectors(int pos, Tree t, boolean typeOK) { if (typeOK && s.token == TYPE) { s.nextToken(); return make.SingletonType(pos, t); } else { t = make.Select(pos, t, ident()); if (s.token == DOT) { t = selectors(s.skipToken(), t, typeOK); } return t; } } /** MixinQualifier ::= `[' Id `]' */ Name mixinQualifierOpt() { if (s.token == LBRACKET) { s.nextToken(); Name name = ident().toTypeName(); accept(RBRACKET); return name; } else { return TypeNames.EMPTY; } } /** StableId ::= Id * | StableRef `.' Id * | [Id '.'] super [MixinQualifier] ` `.' Id */ Tree stableId() { return stableRef(false, false); } /** QualId ::= Id {`.' Id} */ Tree qualId() { Tree id = make.Ident(s.pos, ident()); if (s.token == DOT) return selectors(s.skipToken(), id, false); else return id; } /** SimpleExpr ::= literal * | symbol [ArgumentExprs] * | null */ Tree literal(boolean isPattern) { Tree t; switch (s.token) { case CHARLIT: t = make.Literal(s.pos, new Character((char)s.intVal)); break; case INTLIT: t = make.Literal(s.pos, new Integer((int)s.intVal)); break; case LONGLIT: t = make.Literal(s.pos, new Long(s.intVal)); break; case FLOATLIT: t = make.Literal(s.pos, new Float((float)s.floatVal)); break; case DOUBLELIT: t = make.Literal(s.pos, new Double(s.floatVal)); break; case STRINGLIT: t = make.Literal(s.pos, s.name.toString()); break; case TRUE: t = make.Literal(s.pos, Boolean.TRUE); break; case FALSE: t = make.Literal(s.pos, Boolean.FALSE); break; case NULL: t = make.Ident(s.pos, Names.null_); break; case SYMBOLLIT: Tree symt = scalaDot(s.pos, Names.Symbol); if (isPattern) symt = convertToTypeId(symt); t = make.Apply(s.pos, symt, new Tree[]{make.Literal(s.pos, s.name.toString())}); s.nextToken(); if (s.token == LPAREN || s.token == LBRACE) { Tree labt = scalaDot(s.pos, Names.Labelled); if (isPattern) labt = convertToTypeId(labt); Tree listt = isPattern ? scalaDot(s.pos, Names.List.toTypeName()) : make.Select(s.pos, scalaDot(s.pos, Names.Predef), Names.List); t = make.Apply(s.pos, labt, new Tree[]{t, make.Apply(s.pos, listt, argumentExprs())}); } return t; default: return syntaxError("illegal literal", true); } s.nextToken(); return t; } //////// TYPES /////////////////////////////////////////////////////////////// /** TypedOpt ::= [`:' Type] */ Tree typedOpt() { if (s.token == COLON) { s.nextToken(); return type(); } else { return Tree.Empty; } } /** SimpleTypedOpt ::= [`:' SimpleType] */ Tree simpleTypedOpt() { if (s.token == COLON) { s.nextToken(); return simpleType(); } else { return Tree.Empty; } } /** Types ::= Type {`,' Type} */ Tree[] types() { TreeList ts = new TreeList(); ts.append(type()); while (s.token == COMMA) { s.nextToken(); ts.append(type()); } return ts.toArray(); } /** Type ::= Type1 `=>' Type * | `(' [Types] `)' `=>' Type * | Type1 */ Tree type() { Tree t; if (s.token == LPAREN) { s.nextToken(); if (s.token == RPAREN) { s.nextToken(); int pos = accept(ARROW); return make.FunType(pos, Tree.EMPTY_ARRAY, type()); } else { t = type(); if (s.token == COMMA) { s.nextToken(); TreeList ts = new TreeList(); ts.append(t); ts.append(types()); accept(RPAREN); int pos = accept(ARROW); return make.FunType(pos, ts.toArray(), type()); } else { accept(RPAREN); } } } else { t = type1(); } if (s.token == ARROW) return make.FunType(s.skipToken(), new Tree[]{t}, type()); else return t; } /** Type1 ::= SimpleType {with SimpleType} [Refinement] */ Tree type1() { int pos = s.pos; Tree t = simpleType(); if (s.token == WITH || s.token == LBRACE) { TreeList ts = new TreeList(); ts.append(t); while (s.token == WITH) { s.nextToken(); ts.append(simpleType()); } Tree[] rs = (s.token == LBRACE) ? refinement() : Tree.EMPTY_ARRAY; return make.CompoundType(pos, ts.toArray(), rs); } else { return t; } } /** SimpleType ::= SimpleType TypeArgs * | SimpleType `#' Id * | StableId * | StableRef `.' type * | `(' Type `)' */ Tree simpleType() { int pos = s.pos; Tree t; if (s.token == LPAREN) { s.nextToken(); t = type(); accept(RPAREN); } else { t = convertToTypeId(stableRef(false, true)); } while (true) { if (s.token == HASH) t = make.SelectFromType(s.skipToken(), t, ident().toTypeName()); else if (s.token == LBRACKET) t = make.AppliedType(pos, t, typeArgs()); else break; } return t; } /** TypeArgs ::= `[' Types `]' */ Tree[] typeArgs() { accept(LBRACKET); Tree[] ts = types(); accept(RBRACKET); return ts; } //////// EXPRESSIONS //////////////////////////////////////////////////////// /** EqualsExpr ::= `=' Expr */ Tree equalsExpr() { accept(EQUALS); return expr(); } /** Exprs ::= Expr {`,' Expr} * | Expr `:' `_' `*' */ Tree[] exprs() { TreeList ts = new TreeList(); ts.append(expr(true)); while (s.token == COMMA) { s.nextToken(); ts.append(expr()); } return ts.toArray(); } /** Expr ::= Bindings `=>' Expr * | if `(' Expr `)' Expr [[`;'] else Expr] * | try `{' block `}' [catch Expr] [finally Expr] * | while `(' Expr `)' Expr * | do Expr [`;'] while `(' Expr `)' * | for `(' Enumerators `)' (do | yield) Expr * | throw Expr * | return [Expr] * | [SimpleExpr `.'] Id `=' Expr * | SimpleExpr ArgumentExprs `=' Expr * | PostfixExpr [`:' Type1] * Bindings ::= Id [`:' Type1] * | `(' [Binding {`,' Binding}] `)' * Binding ::= Id [`:' Type] */ Tree expr() { return expr(false); } Tree expr(boolean isArgument) { if (s.token == IF) { int pos = s.skipToken(); accept(LPAREN); Tree cond = expr(); accept(RPAREN); Tree thenp = expr(); Tree elsep = Tree.Empty; if (s.token == ELSE) { s.nextToken(); elsep = expr(); } else { elsep = Tree.Empty; } return make.If(pos, cond, thenp, elsep); } else if (s.token == TRY) { int pos = s.skipToken(); accept(LBRACE); Tree body = block(pos); accept(RBRACE); Tree catcher = Tree.Empty; if (s.token == CATCH) { s.nextToken(); catcher = expr(); } Tree finalizer = Tree.Empty; if (s.token == FINALLY) { s.nextToken(); finalizer = expr(); } return makeTry(pos, body, catcher, finalizer); } else if (s.token == WHILE) { Name lname = Name.fromString("label$" + loopNestingDepth); loopNestingDepth++; int pos = s.skipToken(); accept(LPAREN); Tree cond = expr(); accept(RPAREN); Tree body = expr(); loopNestingDepth--; return makeWhile(pos, lname, cond, body); } else if (s.token == DO) { Name lname = Name.fromString("label$" + loopNestingDepth); loopNestingDepth++; int pos = s.skipToken(); Tree body = expr(); if (s.token == SEMI) s.nextToken(); accept(WHILE); accept(LPAREN); Tree cond = expr(); accept(RPAREN); loopNestingDepth--; return makeDoWhile(pos, lname, body, cond); } else if (s.token == FOR) { s.nextToken(); Tree[] enums; accept(LPAREN); enums = enumerators(); accept(RPAREN); if (s.token == DO) { return makeFor(s.skipToken(), enums, Names.foreach, Names.foreach, expr()); } else if (s.token == YIELD) { return makeFor(s.skipToken(), enums, Names.map, Names.flatmap, expr()); } else { return syntaxError("`do' or `yield' expected", true); } } else if (s.token == RETURN) { int pos = s.skipToken(); Tree e = (isExprIntro()) ? expr() : make.Block(pos, Tree.EMPTY_ARRAY); return make.Return(pos, e); } else if (s.token == THROW) { int pos = s.skipToken(); return make.Throw(pos, expr()); // } else if (s.token == ARROW) { // return make.Function(s.skipToken(), new ValDef[]{}, expr()); } else { Tree t = postfixExpr(); if (s.token == EQUALS) { switch (t) { case Ident(_): case Select(_, _): case Apply(_, _): t = make.Assign(s.skipToken(), t, expr()); } } else if (s.token == COLON) { int pos = s.skipToken(); if (isArgument && s.token == USCORE) { int pos1 = s.skipToken(); if (s.token == IDENTIFIER && s.name == Names.STAR) { s.nextToken(); t = make.Typed( pos, t, make.Ident(pos1, TypeNames.WILDCARD_STAR)); } else { syntaxError(s.pos, "`*' expected", true); } } else { Tree tp = type1(); t = make.Typed(pos, t, tp); } } if (s.token == ARROW) { t = make.Function(s.skipToken(), convertToParams(t), expr()); } return t; } } /** PostfixExpr ::= InfixExpr [Id] * InfixExpr ::= PrefixExpr * | InfixExpr Id InfixExpr */ Tree postfixExpr() { int base = sp; Tree top = prefixExpr(); while (s.token == IDENTIFIER) { top = reduceStack( true, base, top, s.name.precedence(), s.name.isLeftAssoc()); push(top, s.pos, s.name); ident(); if (isExprIntro()) { top = prefixExpr(); } else { sp--; int pos = positions[sp]; Name postOp = operators[sp]; top = reduceStack(true, base, operands[sp], 0, true); return make.Select(pos, top, NameTransformer.encode(postOp)); } } return reduceStack(true, base, top, 0, true); } /** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr */ Tree prefixExpr() { Tree t; if (s.token == IDENTIFIER && (s.name == MINUS || s.name == PLUS || s.name == TILDE || s.name == BANG)) { Name name = ident(); t = make.Select(s.pos, simpleExpr(), name); } else { t = simpleExpr(); } return t; } /* SimpleExpr ::= literal * | StableRef * | `(' [Expr] `)' * | BlockExpr * | new Template * | SimpleExpr `.' Id * | SimpleExpr TypeArgs * | SimpleExpr ArgumentExprs */ Tree simpleExpr() { Tree t; switch (s.token) { case CHARLIT: case INTLIT: case LONGLIT: case FLOATLIT: case DOUBLELIT: case STRINGLIT: case SYMBOLLIT: case TRUE: case FALSE: case NULL: t = literal(false); break; case IDENTIFIER: case THIS: case SUPER: t = stableRef(true, false); break; case LPAREN: int pos = s.skipToken(); if (s.token == RPAREN) { s.nextToken(); t = make.Block(pos, Tree.EMPTY_ARRAY); } else { t = expr(); if (s.token == COMMA) { int commapos = s.skipToken(); TreeList ts = new TreeList(); ts.append(t); ts.append(exprs()); accept(RPAREN); if (s.token == ARROW) { t = make.Function( pos, convertToParams(ts.toArray()), Tree.Empty); } else { t = syntaxError(commapos, "`)' expected", false); } } else { accept(RPAREN); } } break; case LBRACE: t = blockExpr(); break; case NEW: t = make.New(s.skipToken(), template()); break; default: return syntaxError("illegal start of expression", true); } while (true) { switch (s.token) { case DOT: t = make.Select(s.skipToken(), t, ident()); break; case LBRACKET: switch (t) { case Ident(_): case Select(_, _): t = make.TypeApply(s.pos, t, typeArgs()); break; default: return t; } break; case LPAREN: case LBRACE: t = make.Apply(s.pos, t, argumentExprs()); break; default: return t; } } } /** ArgumentExprs ::= `(' [Exprs] `)' * | BlockExpr */ Tree[] argumentExprs() { Tree[] ts = Tree.EMPTY_ARRAY; if (s.token == LBRACE) { ts = new Tree[]{blockExpr()}; } else { accept(LPAREN); if (s.token != RPAREN) ts = exprs(); accept(RPAREN); } return ts; } /** BlockExpr ::= `{' CaseClause {CaseClause} `}' * | `{' Block `}' */ Tree blockExpr() { local++; Tree res; int pos = accept(LBRACE); if (s.token == CASE) { TreeList stats = new TreeList(); do { stats.append(caseClause()); } while (s.token == CASE); res = make.Visitor( pos, (CaseDef[]) stats.copyTo(new CaseDef[stats.length()])); } else { res = block(pos); } accept(RBRACE); local--; return res; } /** Block ::= BlockStatSeq */ Tree block(int pos) { Tree[] stats = blockStatSeq(new TreeList()); if (stats.length == 1 && stats[0].isTerm()) return stats[0]; else return make.Block(pos, stats); } /** CaseClause ::= case Pattern [if PostfixExpr] `=>' Block */ Tree caseClause() { int pos = accept(CASE); Tree pat = validPattern(); Tree guard = Tree.Empty; if (s.token == IF) { s.nextToken(); guard = postfixExpr(); } return make.CaseDef(pos, pat, guard, block(accept(ARROW))); } /** Enumerators ::= Generator {`;' Enumerator} * Enumerator ::= Generator * | Expr */ Tree[] enumerators() { TreeList enums = new TreeList(); enums.append(generator()); while (s.token == SEMI) { s.nextToken(); if (s.token == VAL) enums.append(generator()); else enums.append(expr()); } return enums.toArray(); } /** Generator ::= val Pattern1 `<-' Expr */ Tree generator() { int pos = accept(VAL); Tree pat = validPattern1(); accept(LARROW); Tree rhs = expr(); if (!TreeInfo.isVarPattern(pat)) rhs = make.Apply( rhs.pos, make.Select(rhs.pos, rhs, Names.filter), new Tree[]{ make.Visitor( rhs.pos, new Tree.CaseDef[]{ (CaseDef)make.CaseDef( rhs.pos, pat.duplicate(), Tree.Empty, make.Literal(s.pos, Boolean.TRUE)), (CaseDef)make.CaseDef( rhs.pos, make.Ident(rhs.pos, Names.WILDCARD), Tree.Empty, make.Literal(s.pos, Boolean.FALSE))})}); return make.PatDef(pos, 0, pat, rhs); } //////// PATTERNS //////////////////////////////////////////////////////////// /** Pattern ( see pattern() ) which is checked for validity */ Tree validPattern() { int pos = s.pos; Tree pat = pattern(); if( this.pN.check( pat ) ) { // reports syntax errors as side effect // normalize Tree res = pN.wrapAlternative( pN.elimSequence( pN.flattenSequence ( pat ))); return res; } //syntaxError( pos, "invalid pattern", false ); done in pN.check... return make.Bad(pos); } /** Pattern1 ( see pattern1() ) which is checked for validity */ Tree validPattern1() { int pos = s.pos; Tree pat = pattern1(); if( this.pN.check( pat ) ) { // reports syntax errors as side effect // normalize Tree res = pN.wrapAlternative( pN.elimSequence( pN.flattenSequence ( pat ))); return res; } //syntaxError( pos, "invalid pattern", false ); return make.Bad(pos); } /** Patterns ::= Pattern {`,' Pattern} */ Tree[] patterns() { TreeList ts = new TreeList(); ts.append(pattern()); while (s.token == COMMA) { s.nextToken(); ts.append(pattern()); } return ts.toArray(); } /** Pattern ::= Pattern1 { `|' Pattern1 } */ Tree pattern() { int pos = s.pos; Tree first = pattern1(); if(( s.token == IDENTIFIER )&&( s.name == BAR )) { TreeList choices = new TreeList(); choices.append( first ); while(( s.token == IDENTIFIER )&&( s.name == BAR )) { s.nextToken(); choices.append( pattern1() ); } TreeList ts = pN.flattenAlternativeChildren( choices.toArray() ); return pN.flattenAlternative( make.Alternative( pos, ts.toArray() ) ); } return first; } /** Pattern1 ::= varid `:' Type1 * | `_' `:' Type1 * | Pattern2 */ Tree pattern1() { int base = sp; Tree top = simplePattern(); if (s.token == COLON) { if (TreeInfo.isVarPattern(top)) { return make.Typed(s.skipToken(), top, type1()); } } return pattern1rest(base, top); } /* Pattern2 ::= SimplePattern [ '*' | '?' | '+' ] * | SimplePattern {Id SimplePattern} // op2 must not be empty */ Tree pattern2() { return pattern1rest(sp, simplePattern()); } Tree pattern1rest(int base, Tree top) { if (s.token == IDENTIFIER) { if (s.name == STAR) { /* p* becomes z@( |(p,z)) */ s.nextToken(); Name zname = fresh(); Tree zvar = make.Ident(s.pos, zname); return make.Bind(s.pos, zname, pN.flattenAlternative( make.Alternative(s.pos, new Tree[] { make.Sequence(s.pos, Tree.EMPTY_ARRAY), pN.flattenSequence(make.Sequence(s.pos, new Tree[] { top, zvar })) }))); } else if (s.name == PLUS) { /* p+ becomes z@(p,(z| )) */ s.nextToken(); Name zname = fresh(); Tree zvar = make.Ident(s.pos, zname); return make.Bind(s.pos, zname, pN.flattenSequence(make.Sequence(s.pos, new Tree[] { top, pN.flattenAlternative(make.Alternative(s.pos, new Tree[] { zvar, make.Sequence(s.pos, Tree.EMPTY_ARRAY) })) }))); } else if (s.name == OPT) { /* p? becomes (p| ) */ s.nextToken(); return pN.flattenAlternative(make.Alternative(s.pos, new Tree[] { top, make.Sequence(s.pos, Tree.EMPTY_ARRAY)})); } } while ((s.token == IDENTIFIER) && (s.name != BAR)) { Name tokn = s.name; // for error message top = reduceStack( false, base, top, s.name.precedence(), s.name.isLeftAssoc()); push(top, s.pos, s.name); ident(); top = simplePattern(); if( TreeInfo.isEmptySequence( top ) ) { syntaxError( top.pos, "2nd argument to binary op "+s.name+" may not be empty sequence pattern", false); } } return reduceStack(false, base, top, 0, true); } /** SimplePattern ::= varid [ '@' SimplePattern ] * | `_' * | literal * | StableId [ArgumentPatterns] * | `(' Patterns `)' * | (empty word - nothing) */ Tree simplePattern() { switch (s.token) { case RPAREN: case COMMA: return make.Sequence(s.pos, Tree.EMPTY_ARRAY); // ((nothing)) case IDENTIFIER: if (s.name == BAR) { return make.Sequence(s.pos, Tree.EMPTY_ARRAY); // ((nothing)) } // else fall through to case THIS case THIS: Tree t = stableId(); switch (t) { case Ident(Name name): if ((name.isVariable()) && (s.token == AT)) { int pos = s.pos; s.nextToken(); return make.Bind(pos, name, simplePattern()); } } while (s.token == LPAREN) { t = make.Apply(s.pos, convertToTypeId(t), argumentPatterns()); } return t; case USCORE: return make.Ident(s.skipToken(), Names.WILDCARD); case CHARLIT: case INTLIT: case LONGLIT: case FLOATLIT: case DOUBLELIT: case STRINGLIT: case SYMBOLLIT: case TRUE: case FALSE: case NULL: return literal(true); case LPAREN: int p = s.pos; s.nextToken(); Tree[] ts = patterns(); Tree t; if (ts.length == 1) t = ts[0]; else { t = pN.flattenSequence(make.Sequence(s.pos, ts)); t = pN.elimSequence(t); } accept(RPAREN); return t; default: return syntaxError("illegal start of pattern", true); } } /** ArgumentPatterns ::= `(' [Patterns] `)' */ Tree[] argumentPatterns() { Tree[] ts = Tree.EMPTY_ARRAY; accept(LPAREN); if (s.token != RPAREN) ts = patterns(); accept(RPAREN); return ts; } ////////// MODIFIERS //////////////////////////////////////////////////////////// /** Modifiers ::= {Modifier} * Modifier ::= final * | private * | protected * | override * | abstract */ int modifiers() { pushComment(); int mods = 0; while (true) { int mod; switch (s.token) { case ABSTRACT: mod = Modifiers.ABSTRACT; break; case FINAL: mod = Modifiers.FINAL; break; case SEALED: mod = Modifiers.SEALED; break; case PRIVATE: mod = Modifiers.PRIVATE; break; case PROTECTED: mod = Modifiers.PROTECTED; break; case OVERRIDE: mod = Modifiers.OVERRIDE; break; default: return mods; } if ((mods & mod) != 0) syntaxError(s.pos, "repeated modifier", false); mods |= mod; s.nextToken(); } } /** LocalModifiers ::= {LocalModifier} * LocalModifier ::= final * | private */ int localClassModifiers() { int mods = 0; while (true) { int mod; switch (s.token) { case ABSTRACT: mod = Modifiers.ABSTRACT; break; case FINAL: mod = Modifiers.FINAL; break; case SEALED: mod = Modifiers.SEALED; break; default: return mods; } if ((mods & mod) != 0) syntaxError(s.pos, "repeated modifier", false); mods |= mod; s.nextToken(); } } //////// PARAMETERS ////////////////////////////////////////////////////////// /** ParamClauses ::= {ParamClause} */ ValDef[][] paramClauses() { ArrayList ts = new ArrayList(); while (s.token == LPAREN) ts.add(paramClause()); return (ValDef[][])ts.toArray(new ValDef[ts.size()][]); } /** ParamClauseOpt ::= [ParamClause] */ ValDef[][] paramClauseOpt() { return (s.token == LPAREN) ? new ValDef[][]{paramClause()} : Tree.ValDef_EMPTY_ARRAY_ARRAY; } /** ParamClause ::= `(' [Param {`,' Param}] `)' */ ValDef[] paramClause() { int pos = accept(LPAREN); TreeList params = new TreeList(); if (s.token != RPAREN) { params.append(param()); while (s.token == COMMA) { s.nextToken(); params.append(param()); } } accept(RPAREN); return (ValDef[])params.copyTo(new ValDef[params.length()]); } /** Param ::= [def] Id `:' Type [`*'] */ ValDef param() { int pos = s.pos; int mods = Modifiers.PARAM; if (s.token == DEF) { mods |= Modifiers.DEF; s.nextToken(); } Name name = ident(); accept(COLON); Tree tp = type(); if (s.token == IDENTIFIER && s.name == STAR) { s.nextToken(); mods |= Modifiers.REPEATED; tp = make.AppliedType(tp.pos, scalaDot(tp.pos, Names.Seq.toTypeName()), new Tree[]{tp}); } return (ValDef)make.ValDef(pos, mods, name, tp, Tree.Empty); } /** TypeParamClauseOpt ::= [`[' TypeParam {`,' TypeParam} `]'] * FunTypeParamClauseOpt ::= [`[' FunTypeParam {`,' FunTypeParam} `]'] */ AbsTypeDef[] typeParamClauseOpt(boolean variant) { TreeList params = new TreeList(); if (s.token == LBRACKET) { s.nextToken(); params.append(typeParam(variant)); while (s.token == COMMA) { s.nextToken(); params.append(typeParam(variant)); } accept(RBRACKET); } return (AbsTypeDef[])params.copyTo(new AbsTypeDef[params.length()]); } /** TypeParam ::= [`+' | `-'] FunTypeParam * FunTypeParam ::= Id TypeBounds */ Tree typeParam(boolean variant) { int mods = Modifiers.PARAM; if (variant && s.token == IDENTIFIER) { if (s.name == PLUS) { s.nextToken(); mods |= Modifiers.COVARIANT; } else if (s.name == MINUS) { // syntaxError( // "contravariant type parameters not yet supported", false); s.nextToken(); mods |= Modifiers.CONTRAVARIANT; } } return typeBounds(s.pos, mods, ident()); } /** TypeBounds ::= [`>:' Type] [`<:' Type] */ Tree typeBounds(int pos, int mods, Name name) { Tree lobound; Tree hibound; if (s.token == SUPERTYPE) { s.nextToken(); lobound = type(); } else { lobound = scalaDot(pos, Names.All.toTypeName()); } if (s.token == SUBTYPE) { s.nextToken(); hibound = type(); } else { hibound = scalaDot(pos, Names.Any.toTypeName()); } return make.AbsTypeDef(pos, mods, name.toTypeName(), hibound, lobound); } //////// DEFS //////////////////////////////////////////////////////////////// /** Import ::= import ImportExpr {`,' ImportExpr} */ Tree[] importClause() { accept(IMPORT); TreeList ts = new TreeList(); ts.append(importExpr()); while (s.token == COMMA) { s.nextToken(); ts.append(importExpr()); } return ts.toArray(); } /** ImportRef ::= StableId `.' (Id | `_' | ImportSelectors) */ Tree importExpr() { Tree t; int startpos = s.pos; int pos; if (s.token == THIS) { t = make.This(s.skipToken(), TypeNames.EMPTY); t = make.Select(accept(DOT), t, ident()); pos = accept(DOT); } else { Ident i = make.Ident(s.pos, ident()); pos = accept(DOT); if (s.token == THIS) { s.nextToken(); t = make.This(i.pos, i.name.toTypeName()); t = make.Select(accept(DOT), t, ident()); pos = accept(DOT); } else { t = i; } } while (true) { if (s.token == USCORE) { s.nextToken(); return make.Import(startpos, t, new Name[]{Names.WILDCARD}); } else if (s.token == LBRACE) { return make.Import(startpos, t, importSelectors()); } else { Name name = ident(); if (s.token == DOT) { t = make.Select(pos, t, name); pos = accept(DOT); } else { /* if (name == Names.ASTERISK) s.unit.warning( pos, "this imports only the identifier `*';\nuse `import xyz._' to import all members of `xyz'."); */ return make.Import(startpos, t, new Name[]{name, name}); } } } } /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}' */ Name[] importSelectors() { LinkedList/**/ names = new LinkedList(); accept(LBRACE); boolean isLast = importSelector(names); while (!isLast && s.token == COMMA) { s.nextToken(); isLast = importSelector(names); } accept(RBRACE); return (Name[])names.toArray(new Name[]{}); } /** ImportSelector ::= Id [`=>' Id | `=>' `_'] */ boolean importSelector(LinkedList/**/ names) { if (s.token == USCORE) { s.nextToken(); names.add(Names.WILDCARD); return true; } else { Name name = ident(); names.add(name); if (s.token == ARROW) { s.nextToken(); if (s.token == USCORE) { s.nextToken(); names.add(Names.WILDCARD); } else { names.add(ident()); } } else { names.add(name); } return false; } } /** Def ::= val PatDef {`,' PatDef} * | var VarDef {`,' VarDef} * | def FunDef {`,' FunDef} * | type TypeDef {`,' TypeDef} * | ClsDef * Dcl ::= val ValDcl {`,' ValDcl} * | var ValDcl {`,' ValDcl} * | def FunDcl {`,' FunDcl} * | type TypeDcl {`,' TypeDcl} */ Tree[] defOrDcl(int mods) { TreeList ts = new TreeList(); switch (s.token) { case VAL: do { s.nextToken(); ts.append(popComment(patDefOrDcl(mods))); } while (s.token == COMMA); return ts.toArray(); case VAR: do { s.nextToken(); ts.append(popComment(varDefOrDcl(mods))); } while (s.token == COMMA); return ts.toArray(); case DEF: do { s.nextToken(); ts.append(popComment(funDefOrDcl(mods))); } while (s.token == COMMA); return ts.toArray(); case TYPE: do { s.nextToken(); ts.append(popComment(typeDefOrDcl(mods))); } while (s.token == COMMA); return ts.toArray(); default: return clsDef(mods); } } /** ClsDef ::= ([case] class | trait) ClassDef {`,' ClassDef} * | [case] object ObjectDef {`,' ObjectDef} */ Tree[] clsDef(int mods) { TreeList ts = new TreeList(); switch (s.token) { case CLASS: case CASECLASS: case TRAIT: if (s.token == CASECLASS) mods |= Modifiers.CASE; else if (s.token == TRAIT) mods |= Modifiers.TRAIT | Modifiers.ABSTRACT; do { s.nextToken(); ts.append(classDef(mods)); } while (s.token == COMMA); return ts.toArray(); case OBJECT: case CASEOBJECT: if (s.token == CASEOBJECT) mods |= Modifiers.CASE; do { s.nextToken(); ts.append(objectDef(mods)); } while (s.token == COMMA); return ts.toArray(); default: return new Tree[]{syntaxError("illegal start of definition", true)}; } } /** PatDef ::= Pattern2 [`:' Type] `=' Expr * ValDcl ::= Id `:' Type */ Tree patDefOrDcl(int mods) { int pos = s.pos; Tree pat = pattern2(); Tree tp = (s.token == COLON) ? typedOpt() : Tree.Empty; switch (pat) { case Ident(Name name): if (tp == Tree.Empty || s.token == EQUALS) return make.ValDef(pos, mods, name, tp, equalsExpr()); else return make.ValDef(pos, mods | Modifiers.DEFERRED, name, tp, Tree.Empty); default: return make.PatDef(pos, mods, pat, equalsExpr()); } } /** VarDef ::= Id [`:' Type] `=' Expr * | Id `:' Type `=' `_' * VarDcl ::= Id `:' Type */ Tree varDefOrDcl(int mods) { int pos = s.pos; Name name = ident(); Tree type = typedOpt(); if (type == Tree.Empty || s.token == EQUALS) { accept(EQUALS); Tree rhs; if (type != Tree.Empty && s.token == USCORE) { rhs = Tree.Empty; s.nextToken(); } else { rhs = expr(); } return make.ValDef(pos, mods | Modifiers.MUTABLE, name, type, rhs); } else { return make.ValDef(pos, mods | Modifiers.MUTABLE | Modifiers.DEFERRED, name, type, Tree.Empty); } } /** FunDef ::= Id [FunTypeParamClause] {ParamClauses} [`:' Type] `=' Expr * | this ParamClause `=' ConstrExpr * FunDcl ::= Id [FunTypeParamClause] {ParamClauses} `:' Type */ Tree funDefOrDcl(int mods) { int pos = s.pos; if (s.token == THIS) { s.nextToken(); ValDef[][] vparams = new ValDef[][]{paramClause()}; accept(EQUALS); return make.DefDef( pos, mods, Names.CONSTRUCTOR, Tree.AbsTypeDef_EMPTY_ARRAY, vparams, Tree.Empty, constrExpr()); } else { Name name = ident(); AbsTypeDef[] tparams = typeParamClauseOpt(false); ValDef[][] vparams = paramClauses(); Tree restype = typedOpt(); if (s.token == EQUALS || restype == Tree.Empty) return make.DefDef(pos, mods, name, tparams, vparams, restype, equalsExpr()); else return make.DefDef(pos, mods | Modifiers.DEFERRED, name, tparams, vparams, restype, Tree.Empty); } } /** ConstrExpr ::= SelfInvocation * | `{' SelfInvocation {`;' BlockStat} `}' * SelfInvocation ::= this ArgumentExpr */ Tree constrExpr() { if (s.token == LBRACE) { int pos = s.skipToken(); TreeList statlist = new TreeList(); statlist.append(selfInvocation()); Tree[] stats; if (s.token == SEMI) { s.nextToken(); stats = blockStatSeq(statlist); } else { stats = statlist.toArray(); } accept(RBRACE); return make.Block(pos, stats); } else { return selfInvocation(); } } /** SelfInvocation ::= this ArgumentExprs */ Tree selfInvocation() { int pos = s.pos; accept(THIS); return make.Apply( s.pos, make.Ident(pos, Names.CONSTRUCTOR), argumentExprs()); } /** TypeDef ::= Id `=' Type * TypeDcl ::= Id TypeBounds */ Tree typeDefOrDcl(int mods) { int pos = s.pos; Name name = ident().toTypeName(); switch (s.token) { case LBRACKET: AbsTypeDef[] tparams = typeParamClauseOpt(true); accept(EQUALS); return make.AliasTypeDef(pos, mods, name, tparams, type()); case EQUALS: s.nextToken(); return make.AliasTypeDef(pos, mods, name, Tree.AbsTypeDef_EMPTY_ARRAY, type()); case SUPERTYPE: case SUBTYPE: case SEMI: case COMMA: case RBRACE: return typeBounds(pos, mods | Modifiers.DEFERRED, name); default: return syntaxError("`=', `>:', or `<:' expected", true); } } /** ClassDef ::= Id [TypeParamClause] [ParamClause] [`:' SimpleType] ClassTemplate */ Tree classDef(int mods) { int pos = s.pos; Name clazzname = ident().toTypeName(); AbsTypeDef[] tparams = typeParamClauseOpt(true); ValDef[][] params = paramClauseOpt(); TreeList result = new TreeList(); return popComment(make.ClassDef(pos, mods, clazzname, tparams, params, simpleTypedOpt(), classTemplate())); } /** ObjectDef ::= Id [`:' SimpleType] ClassTemplate */ Tree objectDef(int mods) { return popComment(make.ModuleDef( s.pos, mods, ident(), simpleTypedOpt(), classTemplate())); } /** ClassTemplate ::= [`extends' Constr] {`with' Constr} [TemplateBody] */ Template classTemplate() { int pos = s.pos; if (s.token == EXTENDS) { s.nextToken(); return template(); } else if (s.token == WITH) { s.nextToken(); TreeList parents = new TreeList(); parents.append(scalaObjectConstr(pos)); return template(parents); } else if (s.token == LBRACE) { return (Template)make.Template( pos, new Tree[]{scalaObjectConstr(pos)}, templateBody()); } else { if (!(s.token == SEMI || s.token == COMMA || s.token == RBRACE)) syntaxError("`extends' or `{' expected", true); return (Template)make.Template( pos, new Tree[]{scalaObjectConstr(pos)}, Tree.EMPTY_ARRAY); } } ////////// TEMPLATES //////////////////////////////////////////////////////////// /** Template ::= Constr {`with' Constr} [TemplateBody] */ Template template() { return template(new TreeList()); } Template template(TreeList parents) { int pos = s.pos; parents.append(constr()); while (s.token == WITH) { s.nextToken(); parents.append(constr()); } Tree[] stats = (s.token == LBRACE) ? templateBody() : Tree.EMPTY_ARRAY; return (Template)make.Template(pos, parents.toArray(), stats); } /** Constr ::= StableId [TypeArgs] [`(' [Exprs] `)'] */ Tree constr() { Tree t = convertToConstr(stableId()); if (s.token == LBRACKET) t = make.AppliedType(s.pos, t, typeArgs()); if (s.token == LPAREN) t = make.Apply(s.pos, t, argumentExprs()); return applyConstr(t); } /** TemplateBody ::= `{' [TemplateStat {`;' TemplateStat}] `}' */ Tree[] templateBody() { accept(LBRACE); Tree[] body = templateStatSeq(); if (body.length == 0) body = new Tree[]{Tree.Empty}; accept(RBRACE); return body; } /** Refinement ::= `{' [RefineStat {`;' RefineStat}] `}' */ Tree[] refinement() { accept(LBRACE); Tree[] body = refineStatSeq(); accept(RBRACE); return body; } /////// STATSEQS ////////////////////////////////////////////////////////////// /** Packaging ::= package QualId `{' TopStatSeq `}' */ Tree packaging() { int pos = accept(PACKAGE); Tree pkg = qualId(); accept(LBRACE); Tree[] stats = topStatSeq(); accept(RBRACE); return make.PackageDef(pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, stats)); } /** TopStatSeq ::= [TopStat {`;' TopStat}] * TopStat ::= Modifiers ClsDef * | Packaging * | Import * | */ Tree[] topStatSeq() { TreeList stats = new TreeList(); while (s.token != RBRACE && s.token != EOF) { if (s.token == PACKAGE) { stats.append(packaging()); } else if (s.token == IMPORT) { stats.append(importClause()); } else if (s.token == CLASS || s.token == CASECLASS || s.token == TRAIT || s.token == OBJECT || s.token == CASEOBJECT || isModifier()) { stats.append(clsDef(modifiers())); } else if (s.token != SEMI) { syntaxError("illegal start of class or object definition", true); } if (s.token != RBRACE && s.token != EOF) accept(SEMI); } return stats.toArray(); } /** TemplateStatSeq ::= TemplateStat {`;' TemplateStat} * TemplateStat ::= Import * | Modifiers Def * | Modifiers Dcl * | Expr * | */ Tree[] templateStatSeq() { TreeList stats = new TreeList(); while (s.token != RBRACE && s.token != EOF) { if (s.token == IMPORT) { stats.append(importClause()); } else if (isExprIntro()) { stats.append(expr()); } else if (isDefIntro() || isModifier()) { stats.append(defOrDcl(modifiers())); } else if (s.token != SEMI) { syntaxError("illegal start of definition", true); } if (s.token != RBRACE) accept(SEMI); } return stats.toArray(); } /** RefineStatSeq ::= RefineStat {`;' RefineStat} * RefineStat ::= Dcl * | type TypeDef {`,' TypeDef} * | */ Tree[] refineStatSeq() { TreeList stats = new TreeList(); while (s.token != RBRACE && s.token != EOF) { if (isDclIntro()) { stats.append(defOrDcl(0)); } else if (s.token != SEMI) { syntaxError("illegal start of declaration", true); } if (s.token != RBRACE) accept(SEMI); } return stats.toArray(); } /** BlockStatSeq ::= { BlockStat `;' } [Expr] * BlockStat ::= Import * | Def * | LocalModifiers ClsDef * | Expr * | */ Tree[] blockStatSeq(TreeList stats) { while ((s.token != RBRACE) && (s.token != EOF) && (s.token != CASE)) { if (s.token == IMPORT) { stats.append(importClause()); accept(SEMI); } else if (isExprIntro()) { stats.append(expr()); if (s.token != RBRACE && s.token != CASE) accept(SEMI); } else if (isDefIntro()) { stats.append(defOrDcl(0)); accept(SEMI); if (s.token == RBRACE || s.token == CASE) { stats.append(make.Block(s.pos, Tree.EMPTY_ARRAY)); } } else if (isLocalModifier()) { stats.append(clsDef(localClassModifiers())); accept(SEMI); if (s.token == RBRACE || s.token == CASE) { stats.append(make.Block(s.pos, Tree.EMPTY_ARRAY)); } } else if (s.token == SEMI) { s.nextToken(); } else { syntaxError("illegal start of statement", true); } } return stats.toArray(); } /** CompilationUnit ::= [ package QualId ( `;' | `{' TopStatSeq `}' ) ] TopStatSeq . */ Tree[] compilationUnit() { if (s.token == PACKAGE) { int pos = s.skipToken(); Tree pkg = qualId(); if (s.token == SEMI) { s.nextToken(); return new Tree[]{ make.PackageDef( pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, topStatSeq()))}; } else { TreeList stats = new TreeList(); accept(LBRACE); stats.append( make.PackageDef( pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, topStatSeq()))); accept(RBRACE); stats.append(topStatSeq()); return stats.toArray(); } } else { return topStatSeq(); } } }