diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/ast')
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala | 631 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreeGen.scala | 158 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreeInfo.scala | 187 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreePrinters.scala | 303 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/Trees.scala | 1251 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala | 583 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 1808 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | 908 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala | 349 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala | 25 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Tokens.scala | 97 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala | 289 |
12 files changed, 6589 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala new file mode 100644 index 0000000000..9e4821a734 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -0,0 +1,631 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import scala.concurrent._; +import symtab.Flags._; + + +import java.lang.Math; +import java.util.HashMap; +import java.io.StringWriter; + +import javax.swing.tree._; +import javax.swing.event.TreeModelListener; +import javax.swing._; + +import java.awt.BorderLayout; +import java.awt.{List => awtList, _}; +import java.awt.event._; + +import scala.text._; + +/** + * Tree browsers can show the AST in a graphical and interactive + * way, useful for debugging and understanding. + */ +abstract class TreeBrowsers { + + val global: Global; + import global._; + import nme.EMPTY; + + /** Pseudo tree class, so that all JTree nodes are treated uniformly */ + case class ProgramTree(units: List[UnitTree]) extends Tree { + override def toString(): String = "Program"; + } + + /** Pseudo tree class, so that all JTree nodes are treated uniformly */ + case class UnitTree(unit: CompilationUnit) extends Tree { + override def toString(): String = unit.toString(); + } + + def create(): SwingBrowser = new SwingBrowser(); + + /** + * Java Swing pretty printer for Scala abstract syntax trees. + */ + class SwingBrowser { + + def browse(units: Iterator[CompilationUnit]): Unit = + browse(units.toList); + + /** print the whole program */ + def browse(units: List[CompilationUnit]): Unit = { + val phase: Phase = globalPhase; + var unitList: List[UnitTree] = Nil; + + + for (val i <- units) + unitList = UnitTree(i) :: unitList; + val tm = new ASTTreeModel(ProgramTree(unitList)); + + val frame = new BrowserFrame(); + frame.setTreeModel(tm); + + val lock = new Lock(); + frame.createFrame(lock); + + // wait for the frame to be closed + lock.acquire; + } + } + + /** Tree model for abstract syntax trees */ + class ASTTreeModel(val program: ProgramTree) extends TreeModel { + var listeners: List[TreeModelListener] = Nil; + + /** Add a listener to this tree */ + def addTreeModelListener(l : TreeModelListener): Unit = listeners = l :: listeners; + + /** Return the index'th child of parent */ + def getChild(parent: Any, index: Int): AnyRef = { + packChildren(parent).drop(index).head; + } + + /** Return the number of children this 'parent' has */ + def getChildCount(parent: Any): Int = + packChildren(parent).length; + + /** Return the index of the given child */ + def getIndexOfChild(parent: Any, child: Any): Int = + packChildren(parent).dropWhile(c => c != child).length; + + /** Return the root node */ + def getRoot(): AnyRef = program; + + /** Test whether the given node is a leaf */ + def isLeaf(node: Any): Boolean = packChildren(node).length == 0; + + def removeTreeModelListener(l: TreeModelListener): Unit = + listeners remove (x => x == l); + + /** we ignore this message for now */ + def valueForPathChanged(path: TreePath, newValue: Any) = (); + + /** + * Return a list of children for the given node. + */ + def packChildren(t: Any): List[AnyRef] = + TreeInfo.children(t.asInstanceOf[Tree]); + } + + + /** + * A window that can host the Tree widget and provide methods for + * displaying information + */ + class BrowserFrame { + val frame = new JFrame("Scala AST"); + val topLeftPane = new JPanel(new BorderLayout()); + val topRightPane = new JPanel(new BorderLayout()); + val bottomPane = new JPanel(new BorderLayout()); + var splitPane: JSplitPane = _; + var treeModel: TreeModel = _; + + val textArea: JTextArea = new JTextArea(20, 50); + val infoPanel = new TextInfoPanel(); + + + /** Create a frame that displays the AST. + * + * @param lock The lock is used in order to stop the compilation thread + * until the user is done with the tree inspection. Swing creates its + * own threads when the frame is packed, and therefore execution + * would continue. However, this is not what we want, as the tree and + * especially symbols/types would change while the window is visible. + */ + def createFrame(lock: Lock): Unit = { + lock.acquire; // keep the lock until the user closes the window + + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + frame.addWindowListener(new WindowAdapter() { + /** Release the lock, so compilation may resume after the window is closed. */ + override def windowClosed(e: WindowEvent): Unit = lock.release; + }); + + val tree = new JTree(treeModel) { + /** Return the string for a tree node. */ + override def convertValueToText(value: Any, sel: Boolean, + exp: Boolean, leaf: Boolean, + row: Int, hasFocus: Boolean) = { + val Pair(cls, name) = TreeInfo.treeName(value.asInstanceOf[Tree]); + if (name != EMPTY) + cls + "[" + name.toString() + "]"; + else + cls; + + } + } + + tree.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() { + def valueChanged(e: javax.swing.event.TreeSelectionEvent): Unit = { + textArea.setText(e.getPath().getLastPathComponent().toString()); + infoPanel.update(e.getPath().getLastPathComponent()); + } + }); + + val topSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, topLeftPane, topRightPane); + topSplitPane.setResizeWeight(0.5); + + topLeftPane.add(new JScrollPane(tree), BorderLayout.CENTER); + topRightPane.add(new JScrollPane(infoPanel), BorderLayout.CENTER); + + bottomPane.add(new JScrollPane(textArea), BorderLayout.CENTER); + textArea.setFont(new Font("monospaced", Font.PLAIN, 14)); + textArea.setEditable(false); + + splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, topSplitPane, bottomPane); + frame.getContentPane().add(splitPane); + frame.pack(); + frame.setVisible(true); + } + + def setTreeModel(tm: TreeModel): Unit = treeModel = tm; + } + + /** + * Present detailed information about the selected tree node. + */ + class TextInfoPanel extends JTextArea(30, 40) { + + setFont(new Font("monospaced", Font.PLAIN, 12)); + + def update(v: AnyRef): Unit = { + val t: Tree = v.asInstanceOf[Tree]; + val str = new StringBuffer(); + var buf = new StringWriter(); + + t match { + case ProgramTree(_) => (); + case UnitTree(_) => (); + case _ => + str.append("Symbol: ").append(TreeInfo.symbolText(t)); + str.append("\nSymbol info: \n"); + TreeInfo.symbolTypeDoc(t).format(getWidth() / getColumnWidth(), buf); + str.append(buf.toString()); + str.append("\nSymbol tpe: \n"); + if (t.symbol != null) { + buf = new StringWriter(); + TypePrinter.toDocument(t.symbol.tpe).format(getWidth() / getColumnWidth(), buf); + str.append(buf.toString()); + } + str.append("\nSymbol Attributes: \n").append(TreeInfo.symbolAttributes(t)); + str.append("\nType: \n").append(if (t.tpe ne null) t.tpe.toString() else ""); + } + setText(str.toString()); + } + } + + + /** Computes different information about a tree node. It + * is used as central place to do all pattern matching against + * Tree. + */ + object TreeInfo { + + /** Return the case class name and the Name, if the node defines one */ + def treeName(t: Tree): Pair[String, Name] = t match { + case ProgramTree(units) => + Pair("Program", EMPTY); + + case UnitTree(unit) => + Pair("CompilationUnit", unit.toString()); + + case Attributed(attribute, definition) => + Pair("Attributed", EMPTY); + + case DocDef(comment, definition) => + Pair("DocDef", EMPTY); + + case ClassDef(mods, name, tparams, tpt, impl) => + Pair("ClassDef", name); + + case PackageDef(packaged, impl) => + Pair("PackageDef", EMPTY); + + case ModuleDef(mods, name, impl) => + Pair("ModuleDef", name); + + case ValDef(mods, name, tpe, rhs) => + Pair("ValDef", name); + + case DefDef(mods, name, tparams, vparams, tpe, rhs) => + Pair("DefDef", name); + + case AbsTypeDef(mods, name, rhs, lobound) => + Pair("AbsTypeDef", name); + + case AliasTypeDef(mods, name, tparams, rhs) => + Pair("AliasTypeDef", name); + + case Import(expr, selectors) => + Pair("Import", EMPTY); + + case CaseDef(pat, guard, body) => + Pair("CaseDef", EMPTY); + + case Template(parents, body) => + Pair("Template", EMPTY); + + case LabelDef(name, params, rhs) => + Pair("LabelDef", name); + + case Block(stats, expr) => + Pair("Block", EMPTY); + + case Sequence(trees) => + Pair("Sequence", EMPTY); + + case Alternative(trees) => + Pair("Alternative", EMPTY); + + case Bind(name, rhs) => + Pair("Bind", name); + + case Match(selector, cases) => + Pair("Visitor", EMPTY); + + case Function(vparams, body) => + Pair("Function", EMPTY); + + case Assign(lhs, rhs) => + Pair("Assign", EMPTY); + + case If(cond, thenp, elsep) => + Pair("If", EMPTY); + + case Return(expr) => + Pair("Return", EMPTY); + + case Throw(expr) => + Pair("Throw", EMPTY); + + case New(init) => + Pair("New", EMPTY); + + case Typed(expr, tpe) => + Pair("Typed", EMPTY); + + case TypeApply(fun, args) => + Pair("TypeApply", EMPTY); + + case Apply(fun, args) => + Pair("Apply", EMPTY); + + case Super(qualif, mixin) => + Pair("Super", qualif.toString() + ", mixin: " + mixin.toString()); + + case This(qualifier) => + Pair("This", qualifier); + + case Select(qualifier, selector) => + Pair("Select", selector); + + case Ident(name) => + Pair("Ident", name); + + case Literal(value) => + Pair("Literal", EMPTY); + + case TypeTree() => + Pair("TypeTree", EMPTY); + + case SingletonTypeTree(ref) => + Pair("SingletonType", EMPTY); + + case SelectFromTypeTree(qualifier, selector) => + Pair("SelectFromType", selector); + + case CompoundTypeTree(template) => + Pair("CompoundType", EMPTY); + + case AppliedTypeTree(tpe, args) => + Pair("AppliedType", EMPTY); + + case Try(block, catcher, finalizer) => + Pair("Try", EMPTY); + + case EmptyTree => + Pair("Empty", EMPTY); + + case ArrayValue(elemtpt, trees) => + Pair("ArrayValue", EMPTY); + } + + /** Return a list of children for the given tree node */ + def children(t: Tree): List[Tree] = t match { + case ProgramTree(units) => + units; + + case UnitTree(unit) => + List(unit.body); + + case Attributed(attribute, definition) => + List(attribute, definition); + + case DocDef(comment, definition) => + List(definition); + + case ClassDef(mods, name, tparams, tpt, impl) => { + var children: List[Tree] = List(); + children = tparams ::: children; + tpt :: impl :: children + } + + case PackageDef(name, stats) => + stats; + + case ModuleDef(mods, name, impl) => + List(impl); + + case ValDef(mods, name, tpe, rhs) => + List(tpe, rhs); + + case DefDef(mods, name, tparams, vparams, tpe, rhs) => { + var children: List[Tree] = List(); + children = tparams ::: children; + children = List.flatten(vparams) ::: children; + tpe :: rhs :: children + } + + case AbsTypeDef(mods, name, rhs, lobound) => + List(rhs, lobound); + + case AliasTypeDef(mods, name, tparams, rhs) => { + var children: List[Tree] = List(); + children = tparams ::: children; + rhs :: children + } + + case Import(expr, selectors) => { + var children: List[Tree] = List(expr); + children + } + + case CaseDef(pat, guard, body) => + List(pat, guard, body); + + case Template(parents, body) => + parents ::: body; + + case LabelDef(name, params, rhs) => + params ::: List(rhs); + + case Block(stats, expr) => + stats ::: List(expr); + + case Sequence(trees) => + trees; + + case Alternative(trees) => + trees; + + case Bind(name, rhs) => + List(rhs); + + case Match(selector, cases) => + selector :: cases; + + case Function(vparams, body) => + vparams ::: List(body); + + case Assign(lhs, rhs) => + List(lhs, rhs); + + case If(cond, thenp, elsep) => + List(cond, thenp, elsep); + + case Return(expr) => + List(expr); + + case Throw(expr) => + List(expr); + + case New(init) => + List(init); + + case Typed(expr, tpe) => + List(expr, tpe); + + case TypeApply(fun, args) => + List(fun) ::: args; + + case Apply(fun, args) => + List(fun) ::: args; + + case Super(qualif, mixin) => + Nil; + + case This(qualif) => + Nil + + case Select(qualif, selector) => + List(qualif); + + case Ident(name) => + Nil; + + case Literal(value) => + Nil; + + case TypeTree() => + Nil; + + case SingletonTypeTree(ref) => + List(ref); + + case SelectFromTypeTree(qualif, selector) => + List(qualif); + + case CompoundTypeTree(templ) => + List(templ); + + case AppliedTypeTree(tpe, args) => + tpe :: args; + + case Try(block, catches, finalizer) => + block :: catches ::: List(finalizer); + + case ArrayValue(elemtpt, elems) => + elemtpt :: elems; + + case EmptyTree => + Nil; + } + + /** Return a textual representation of this t's symbol */ + def symbolText(t: Tree): String = { + var prefix = ""; + + if (t.hasSymbol) + prefix = "[has] "; + if (t.isDef) + prefix = "[defines] "; + + prefix + t.symbol + } + + /** Return t's symbol type */ + def symbolTypeDoc(t: Tree): Document = { + val s = t.symbol; + if (s != null) + TypePrinter.toDocument(s.info); + else + DocNil; + } + + /** Return a textual representation of (some of) the symbol's + * attributes */ + def symbolAttributes(t: Tree): String = { + val s = t.symbol; + var att = ""; + + if (s != null) { + var str = flagsToString(s.flags); + if (s.hasFlag(STATIC) || s.hasFlag(STATICMEMBER)) + str = str + " isStatic "; + str + } + else ""; + } + } + + object TypePrinter { + + ///////////////// Document pretty printer //////////////// + + implicit def view(n: String): Document = DocText(n); + + def toDocument(sym: Symbol): Document = + toDocument(sym.info); + + def symsToDocument(syms: List[Symbol]): Document = syms match { + case Nil => DocNil; + case s :: Nil => Document.group(toDocument(s)); + case _ => + Document.group( + syms.tail.foldLeft (toDocument(syms.head) :: ", ") ( + (d: Document, s2: Symbol) => toDocument(s2) :: ", " :/: d) ); + } + + def toDocument(ts: List[Type]): Document = ts match { + case Nil => DocNil; + case t :: Nil => Document.group(toDocument(t)); + case _ => + Document.group( + ts.tail.foldLeft (toDocument(ts.head) :: ", ") ( + (d: Document, t2: Type) => toDocument(t2) :: ", " :/: d) ); + } + + def toDocument(t: Type): Document = t match { + case ErrorType => "ErrorType()"; + case WildcardType => "WildcardType()"; + case NoType => "NoType()"; + case NoPrefix => "NoPrefix()"; + case ThisType(s) => "ThisType(" + s.name + ")"; + + case SingleType(pre, sym) => + Document.group( + Document.nest(4, "SingleType(" :/: + toDocument(pre) :: ", " :/: sym.name.toString() :: ")") + ); + + case ConstantType(value) => + "ConstantType(" + value + ")"; + + case TypeRef(pre, sym, args) => + Document.group( + Document.nest(4, "TypeRef(" :/: + toDocument(pre) :: ", " :/: + sym.name.toString() :: ", " :/: + "[ " :: toDocument(args) ::"]" :: ")") + ); + + case TypeBounds(lo, hi) => + Document.group( + Document.nest(4, "TypeBounds(" :/: + toDocument(lo) :: ", " :/: + toDocument(hi) :: ")") + ); + + case RefinedType(parents, defs) => + Document.group( + Document.nest(4, "RefinedType(" :/: + toDocument(parents) :: ")") + ); + + case ClassInfoType(parents, defs, clazz) => + Document.group( + Document.nest(4,"ClassInfoType(" :/: + toDocument(parents) :: ", " :/: + clazz.name.toString() :: ")") + ); + + case MethodType(paramtypes, result) => + Document.group( + Document.nest(4, "MethodType(" :/: + Document.group("(" :/: + toDocument(paramtypes) :/: + "), ") :/: + toDocument(result) :: ")") + ); + + case PolyType(tparams, result) => + Document.group( + Document.nest(4,"PolyType(" :/: + Document.group("(" :/: + symsToDocument(tparams) :/: + "), ") :/: + toDocument(result) :: ")") + ); + + case _ => abort("Unknown case: " + t.toString()); + } + } + +} diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala new file mode 100644 index 0000000000..425cf9eac8 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -0,0 +1,158 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import scala.tools.nsc.util.Position; +import symtab.Flags._; + +abstract class TreeGen { + + val global: Global; + + import global._; + import definitions._; + import posAssigner.atPos; + + /** Builds a reference to value whose type is given stable prefix. + */ + def mkQualifier(tpe: Type): Tree = tpe match { + case NoPrefix => + EmptyTree + case ThisType(clazz) => + if (clazz.isRoot || clazz.isEmptyPackageClass) EmptyTree else This(clazz) + case SingleType(pre, sym) => + if (sym.isThisSkolem) { + mkQualifier(ThisType(sym.deSkolemize)) + } else { + val qual = mkStableRef(pre, sym); + qual.tpe match { + case MethodType(List(), restpe) => + Apply(qual, List()) setType restpe + case _ => + qual + } + } + case TypeRef(pre, sym, args) => + assert(phase.erasedTypes); + if (sym.isModuleClass && !sym.isRoot) { + val qual = Select(mkQualifier(sym.owner.tpe), sym.sourceModule); + qual.tpe match { + case MethodType(List(), restpe) => + Apply(qual, List()) setType restpe + case _ => + qual + } + } else This(sym) + } + + /** Builds a reference to given symbol with given stable prefix. */ + def mkRef(pre: Type, sym: Symbol): Tree = { + val qual = mkQualifier(pre); + if (qual == EmptyTree) Ident(sym) else Select(qual, sym) + } + + /** Builds a reference to given symbol. */ + def mkRef(sym: Symbol): Tree = + if (sym.owner.isClass) mkRef(sym.owner.thisType, sym) else Ident(sym); + + /** Replaces tree type with a stable type if possible */ + def stabilize(tree: Tree): Tree = tree match { + case Ident(_) => + if (tree.symbol.isStable) tree.setType(singleType(tree.symbol.owner.thisType, tree.symbol)) + else tree + case Select(qual, _) => + if (tree.symbol.isStable && qual.tpe.isStable) tree.setType(singleType(qual.tpe, tree.symbol)) + else tree + case _ => + tree + } + + /** Cast `tree' to type `pt' */ + def cast(tree: Tree, pt: Type): Tree = { + if (settings.debug.value) log("casting " + tree + ":" + tree.tpe + " to " + pt); + assert(!tree.tpe.isInstanceOf[MethodType], tree); + typer.typed { + atPos(tree.pos) { + Apply(TypeApply(Select(tree, Object_asInstanceOf), List(TypeTree(pt))), List()) + } + } + } + + /** Builds a reference with stable type to given symbol */ + def mkStableRef(pre: Type, sym: Symbol): Tree = stabilize(mkRef(pre, sym)); + def mkStableRef(sym: Symbol): Tree = stabilize(mkRef(sym)); + + def This(sym: Symbol): Tree = + global.This(sym.name) setSymbol sym setType sym.thisType; + + def Ident(sym: Symbol): Tree = { + assert(sym.isTerm); + global.Ident(sym.name) setSymbol sym setType sym.tpe; + } + + def Select(qual: Tree, sym: Symbol): Tree = + if (qual.symbol != null && + (qual.symbol.name.toTermName == nme.ROOT || + qual.symbol.name.toTermName == nme.EMPTY_PACKAGE_NAME)) { + this.Ident(sym) + } else { + assert(sym.isTerm); + val result = global.Select(qual, sym.name) setSymbol sym; + if (qual.tpe != null) result setType qual.tpe.memberType(sym); + result + } + + /** Builds an instance test with given value and type. */ + def mkIsInstanceOf(value: Tree, tpe: Type, erased: Boolean): Tree = { + val sym = + if(erased) + definitions.Any_isInstanceOfErased + else + definitions.Any_isInstanceOf; + Apply( + TypeApply( + Select(value, sym), + List(TypeTree(tpe))), + List()) + } + + def mkIsInstanceOf(value: Tree, tpe: Type): Tree = { + mkIsInstanceOf(value, tpe, global.phase.erasedTypes); + } + + /** Builds a cast with given value and type. */ + def mkAsInstanceOf(value: Tree, tpe: Type, erased: Boolean): Tree = { + val sym = + if(erased) + definitions.Any_asInstanceOfErased + else + definitions.Any_asInstanceOf; + + Apply( + TypeApply( + Select(value, sym), + List(TypeTree(tpe))), + List()) + } + + def mkAsInstanceOf(value: Tree, tpe: Type): Tree = { + mkAsInstanceOf(value, tpe, global.phase.erasedTypes); + } + + + /** Builds a list with given head and tail. */ + def mkNewCons(head: Tree, tail: Tree): Tree = + New(Apply(mkRef(definitions.ConsClass), List(head,tail))); + + /** Builds a list with given head and tail. */ + def mkNil: Tree = + mkRef(definitions.NilModule); + + /** Builds a pair */ + def mkNewPair(left: Tree, right: Tree) = + New(Apply(mkRef(definitions.TupleClass(2)), List(left,right))); + +} diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala new file mode 100644 index 0000000000..dedfc6b03a --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala @@ -0,0 +1,187 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import symtab.Flags._; + +abstract class TreeInfo { + + val global: Global; + import global._; + + def isTerm(tree: Tree): boolean = tree.isTerm; + def isType(tree: Tree): boolean = tree.isType; + + def isOwnerDefinition(tree: Tree): boolean = tree match { + case PackageDef(_, _) + | ClassDef(_, _, _, _, _) + | ModuleDef(_, _, _) + | DefDef(_, _, _, _, _, _) + | Import(_, _) => true + case _ => false + } + + def isDefinition(tree: Tree): boolean = tree.isDef; + + def isDeclaration(tree: Tree): boolean = tree match { + case DefDef(_, _, _, _, _, EmptyTree) + | ValDef(_, _, _, EmptyTree) + | AbsTypeDef(_, _, _, _) + | AliasTypeDef(_, _, _, _) => true + case _ => false + } + + /** Is tree legal as a member definition of an interface? + */ + def isInterfaceMember(tree: Tree): boolean = tree match { + case EmptyTree => true + case Import(_, _) => true + case AbsTypeDef(_, _, _, _) => true + case AliasTypeDef(_, _, _, _) => true + case DefDef(mods, _, _, _, _, __) => mods.hasFlag(DEFERRED) + case ValDef(mods, _, _, _) => mods.hasFlag(DEFERRED) + case DocDef(_, definition) => isInterfaceMember(definition) + case Attributed(_, definition) => isInterfaceMember(definition) + case _ => false + } + + + /** Is tree a pure definition? + */ + def isPureDef(tree: Tree): boolean = tree match { + case EmptyTree + | ClassDef(_, _, _, _, _) + | ModuleDef(_, _, _) + | AbsTypeDef(_, _, _, _) + | AliasTypeDef(_, _, _, _) + | Import(_, _) + | DefDef(_, _, _, _, _, _) => + true + case ValDef(mods, _, _, rhs) => + mods.hasFlag(MUTABLE) && isPureExpr(rhs) + case DocDef(_, definition) => + isPureDef(definition) + case Attributed(_, definition) => + isPureDef(definition) + case _ => + false + } + + /** Is tree a stable & pure expression? + */ + def isPureExpr(tree: Tree): boolean = tree match { + case EmptyTree + | This(_) + | Super(_, _) + | Literal(_) => + true + case Ident(_) => + tree.symbol.isStable + case Select(qual, _) => + tree.symbol.isStable && isPureExpr(qual) + case TypeApply(fn, _) => + isPureExpr(fn) + case Apply(fn, List()) => + isPureExpr(fn) + case Typed(expr, _) => + isPureExpr(expr) + case Block(stats, expr) => + (stats forall isPureDef) && isPureExpr(expr) + case _ => + false + } + + /** Is tree a self constructor call? + */ + def isSelfConstrCall(tree: Tree): boolean = tree match { + case Ident(nme.CONSTRUCTOR) => + true + case TypeApply(constr, _) => + isSelfConstrCall(constr) + case Apply(constr, _) => + isSelfConstrCall(constr) + case _ => + false + } + + /** Is tree a variable pattern */ + def isVarPattern(pat: Tree): boolean = pat match { + case Ident(name) => isVariableName(name) + case _ => false + } + + /** The longest statement suffix that starts with a constructor */ + def firstConstructor(stats: List[Tree]): Tree = stats.head match { + case constr @ DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => constr + case _ => firstConstructor(stats.tail) + } + + /** Is name a left-associative operator? */ + def isLeftAssoc(operator: Name): boolean = + operator.length > 0 && operator(operator.length - 1) != ':'; + + /** Is name a variable name? */ + def isVariableName(name: Name): boolean = { + val first = name(0); + ((('a' <= first && first <= 'z') || first == '_') + && name != nme.false_ + && name != nme.true_ + && name != nme.null_) + } + + /** Is tree a this node which belongs to `enclClass'? */ + def isSelf(tree: Tree, enclClass: Symbol): boolean = tree match { + case This(_) => tree.symbol == enclClass + case _ => false + } + + /** Is this pattern node a catch-all (wildcard or variable) pattern? */ + def isDefaultCase(cdef: CaseDef) = cdef match { + case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true + case CaseDef(Bind(_, Ident(nme.WILDCARD)), EmptyTree, _) => true + case _ => false + } + + /** Is this pattern node a catch-all or type-test pattern? */ + def isCatchCase(cdef: CaseDef) = cdef match { + case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) => isSimple(tpt.tpe) + case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) => isSimple(tpt.tpe) + case _ => isDefaultCase(cdef) + } + + private def isSimple(tp: Type): boolean = true; + /* If we have run-time types, and these are used for pattern matching, + we should replace this by something like: + + tp match { + case TypeRef(pre, sym, args) => + args.isEmpty && (sym.owner.isPackageClass || isSimple(pre)) + case NoPrefix => + true + case _ => + false + } +*/ + + /** Is this pattern node a sequence-valued pattern? */ + def isSequenceValued(tree: Tree): boolean = tree match { + case Bind(_, body) => isSequenceValued(body) + case Sequence(_) => true + case ArrayValue(_, _) => true + case Star(_) => true + case Alternative(ts) => ts exists isSequenceValued + case _ => false + } + + /** The method part of an application node + */ + def methPart(tree: Tree): Tree = tree match { + case Apply(fn, _) => methPart(fn) + case TypeApply(fn, _) => methPart(fn) + case AppliedTypeTree(fn, _) => methPart(fn) + case _ => tree + } +} diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala new file mode 100644 index 0000000000..206fa58061 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala @@ -0,0 +1,303 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import java.io._; +import symtab.Flags._; + +abstract class TreePrinters { + + val global: Global; + import global._; + + class TreePrinter(out: PrintWriter) { + protected var indentMargin = 0; + protected val indentStep = 2; + protected var indentString = " "; + + def flush = out.flush(); + + def indent = indentMargin = indentMargin + indentStep; + def undent = indentMargin = indentMargin - indentStep; + + def println = { + out.println(); + while (indentMargin > indentString.length()) + indentString = indentString + indentString; + if (indentMargin > 0) + out.write(indentString, 0, indentMargin); + } + + def printSeq[a](ls: List[a])(printelem: a => unit)(printsep: => unit): unit = ls match { + case List() => + case List(x) => printelem(x) + case x :: rest => printelem(x); printsep; printSeq(rest)(printelem)(printsep) + } + + def printColumn(ts: List[Tree], start: String, sep: String, end: String): unit = { + print(start); indent; println; + printSeq(ts){print}{print(sep); println}; undent; println; print(end) + } + + def printRow(ts: List[Tree], start: String, sep: String, end: String): unit = { + print(start); printSeq(ts){print}{print(sep)}; print(end) + } + + def printRow(ts: List[Tree], sep: String): unit = printRow(ts, "", sep, ""); + + def printTypeParams(ts: List[AbsTypeDef]): unit = + if (!ts.isEmpty) { + print("["); printSeq(ts){printParam}{print(", ")}; print("]") + } + + def printValueParams(ts: List[ValDef]): unit = { + print("("); + if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, nme.EMPTY.toTypeName); + printSeq(ts){printParam}{print(", ")}; + print(")") + } + + def printParam(tree: Tree): unit = tree match { + case ValDef(mods, name, tp, rhs) => + print(symName(tree, name)); printOpt(": ", tp); + case AbsTypeDef(mods, name, lo, hi) => + print(symName(tree, name)); + printOpt(" >: ", lo); printOpt(" <: ", hi); + } + + def printBlock(tree: Tree): unit = tree match { + case Block(_, _) => print(tree) + case _ => printColumn(List(tree), "{", ";", "}") + } + + def symName(tree: Tree, name: Name): String = + if (tree.symbol != NoSymbol) tree.symbol.nameString else name.toString(); + + def printOpt(prefix: String, tree: Tree): unit = + if (!tree.isEmpty) { print(prefix); print(tree) } + + def printModifiers(tree: Tree, mods: Modifiers): unit = { + if (tree.symbol == NoSymbol) + printFlags(mods.flags, mods.privateWithin) + else if (tree.symbol.privateWithin == null) + printFlags(tree.symbol.flags, nme.EMPTY.toTypeName) + else + printFlags(tree.symbol.flags, tree.symbol.privateWithin.name) + } + + def printFlags(flags: long, privateWithin: Name): unit = { + val mask = if (settings.debug.value) -1 else PrintableFlags; + val suffixes: List[Pair[long, String]] = + if (privateWithin.isEmpty) List() else List(Pair(PRIVATE, privateWithin.toString())); + val s = flagsToString(flags & mask, suffixes); + if (s.length() != 0) print(s + " ") + } + + def print(str: String): unit = out.print(str); + def print(name: Name): unit = print(name.toString()); + + def printRaw(tree: Tree): unit = { + tree match { + case EmptyTree => + print("<empty>"); + + case ClassDef(mods, name, tparams, tp, impl) => + printModifiers(tree, mods); + print((if (mods.hasFlag(TRAIT)) "trait " else "class ") + symName(tree, name)); + printTypeParams(tparams); + printOpt(": ", tp); print(" extends "); print(impl); + + case PackageDef(packaged, stats) => + print("package "); print(packaged); printColumn(stats, " {", ";", "}") + + case ModuleDef(mods, name, impl) => + printModifiers(tree, mods); print("object " + symName(tree, name)); + print(" extends "); print(impl); + + case ValDef(mods, name, tp, rhs) => + printModifiers(tree, mods); + print(if (mods.hasFlag(MUTABLE)) "var " else "val "); + print(symName(tree, name)); + printOpt(": ", tp); + if (!mods.hasFlag(DEFERRED)) { + print(" = "); + if (rhs.isEmpty) print("_") else print(rhs) + } + + case DefDef(mods, name, tparams, vparamss, tp, rhs) => + printModifiers(tree, mods); + print("def " + symName(tree, name)); + printTypeParams(tparams); vparamss foreach printValueParams; + printOpt(": ", tp); printOpt(" = ", rhs); + + case AbsTypeDef(mods, name, lo, hi) => + printModifiers(tree, mods); print("type "); printParam(tree); + + case AliasTypeDef(mods, name, tparams, rhs) => + printModifiers(tree, mods); print("type " + symName(tree, name)); + printTypeParams(tparams); printOpt(" = ", rhs); + + case LabelDef(name, params, rhs) => + print(symName(tree, name)); printRow(params, "(", ",", ")"); printBlock(rhs); + + case Import(expr, selectors) => + def selectorToString(s: Pair[Name, Name]): String = + if (s._1 == nme.WILDCARD || s._1 == s._2) s._1.toString() + else s._1.toString() + "=>" + s._2.toString(); + print("import "); print(expr); + print(selectors.map(selectorToString).mkString(".{", ", ", "}")) + + case Attributed(attr, definition) => + print("["); print(attr); print("]"); println; print(definition); + + case DocDef(comment, definition) => + print(comment); println; print(definition); + + case Template(parents, body) => + printRow(parents, " with "); + if (!body.isEmpty) printColumn(body, " {", ";", "}") + + case Block(stats, expr) => + printColumn(stats ::: List(expr), "{", ";", "}") + + case Match(selector, cases) => + print(selector); printColumn(cases, " match {", "", "}") + + case CaseDef(pat, guard, body) => + print("case "); print(pat); printOpt(" if ", guard); + print(" => "); print(body) + + case Sequence(trees) => + printRow(trees, "[", ", ", "]") + + case Alternative(trees) => + printRow(trees, "(", "| ", ")") + + case Star(elem) => + print("("); print(elem); print(")*"); + + case Bind(name, t) => + print("("); print(symName(tree, name)); print(" @ "); print(t); print(")"); + + case ArrayValue(elemtpt, trees) => + print("Array["); print(elemtpt); printRow(trees, "]{", ", ", "}") + + case Function(vparams, body) => + print("("); printValueParams(vparams); print(" => "); print(body); print(")") + + case Assign(lhs, rhs) => + print(lhs); print(" = "); print(rhs) + + case If(cond, thenp, elsep) => + print("if ("); print(cond); print(")"); indent; println; + print(thenp); undent; + if (!elsep.isEmpty) { + println; print("else"); indent; println; print(elsep); undent + } + + case Return(expr) => + print("return "); print(expr) + + case Try(block, catches, finalizer) => + print("try "); printBlock(block); + if (!catches.isEmpty) printColumn(catches, " catch {", "", "}"); + printOpt(" finally ", finalizer) + + case Throw(expr) => + print("throw "); print(expr) + + case New(tpe) => + print("new "); print(tpe) + + case Typed(expr, tp) => + print("("); print(expr); print(") : "); print(tp); + + case TypeApply(fun, targs) => + print(fun); printRow(targs, "[", ", ", "]"); + + case Apply(fun, vargs) => + print(fun); printRow(vargs, "(", ", ", ")"); + + case Super(qual, mixin) => + if (!qual.isEmpty || tree.symbol != NoSymbol) print(symName(tree, qual) + "."); + print("super"); + if (!mixin.isEmpty) + print("[" + mixin + "]") + + case This(qual) => + if (!qual.isEmpty) print(symName(tree, qual) + "."); + print("this"); + + case Select(qualifier, name) => + print(qualifier); print("."); print(symName(tree, name)) + + case Ident(name) => + print(symName(tree, name)) + + case Literal(x) => + print(x.tag match { + case NullTag => "null" + case StringTag => "\"" + x.stringValue + "\"" + case CharTag => "\'" + x.charValue + "\'" + case LongTag => x.longValue.toString() + "L"; + case _ => x.value.toString() + }) + + case TypeTree() => + if (tree.tpe == null) print("<type ?>") + else print(tree.tpe.toString()); + + case SingletonTypeTree(ref) => + print(ref); print(".type") + + case SelectFromTypeTree(qualifier, selector) => + print(qualifier); print("#"); print(symName(tree, selector)) + + case CompoundTypeTree(templ) => + print(templ) + + case AppliedTypeTree(tp, args) => + print(tp); printRow(args, "[", ", ", "]") + } + if (global.settings.printtypes.value && tree.isTerm && !tree.isEmpty) { + print("{"); print(if (tree.tpe == null) "<null>" else tree.tpe.toString()); print("}") + } + } + + def print(tree: Tree): unit = + printRaw( + if (tree.isDef && tree.symbol != NoSymbol) { + tree match { + case ClassDef(_, _, _, _, impl) => ClassDef(tree.symbol, impl) + case ModuleDef(_, _, impl) => ModuleDef(tree.symbol, impl) +// case ValDef(_, _, _, rhs) => ValDef(tree.symbol, rhs) + case DefDef(_, _, _, vparamss, _, rhs) => DefDef(tree.symbol, vparamss, rhs) + case AbsTypeDef(_, _, _, _) => AbsTypeDef(tree.symbol) + case AliasTypeDef(_, _, _, rhs) => AliasTypeDef(tree.symbol, rhs) + case _ => tree + } + } else tree); + + def print(unit: CompilationUnit): unit = { + print("// Scala source: " + unit.source + "\n"); + if (unit.body != null) { + print(unit.body); println + } else { + print("<null>") + } + println; flush + } + + def printAll(): unit = { + print("[[syntax trees at end of " + phase + "]]"); + for (val unit <- global.currentRun.units) print(unit) + } + } + + def create(writer: PrintWriter): TreePrinter = new TreePrinter(writer); + def create(stream: OutputStream): TreePrinter = create(new PrintWriter(stream)); + def create(): TreePrinter = create(System.out); +} diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala new file mode 100644 index 0000000000..92b147c947 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -0,0 +1,1251 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast; + +import scala.tools.nsc.symtab.Flags; +import java.io.StringWriter; +import java.io.PrintWriter; +import scala.tools.nsc.util.{Position,SourceFile}; +import symtab.Flags._; + +[_trait_] abstract class Trees: Global { + + //statistics + var nodeCount = 0; + + case class Modifiers(flags: int, privateWithin: Name) { + def isPrivate = ((flags & PRIVATE ) != 0); + def isProtected = ((flags & PROTECTED) != 0); + def isVariable = ((flags & MUTABLE) != 0); + def isPublic = !isPrivate && !isProtected; + def hasFlag(flag: int) = (flags & flag) != 0; + def | (flag: int): Modifiers = { + val flags1 = flags | flag; + if (flags1 == flags) this else Modifiers(flags1, privateWithin) + } + } + + def Modifiers(flags: int): Modifiers = Modifiers(flags, nme.EMPTY.toTypeName); + def Modifiers(flags: long): Modifiers = Modifiers(flags.asInstanceOf[int]); + + val NoMods = Modifiers(0); + + abstract class Tree { + + if (util.Statistics.enabled) nodeCount = nodeCount + 1; + + private var posx: int = Position.NOPOS; + + def pos = posx; + + var tpe: Type = _; + + def setPos(p: int): this.type = { posx = p; this } + def setType(tp: Type): this.type = { tpe = tp; this } + + def symbol: Symbol = null; + def symbol_=(sym: Symbol): unit = + throw new Error("symbol_= inapplicable for " + this); + def setSymbol(sym: Symbol): this.type = { symbol = sym; this } + + def hasSymbol = false; + def isDef = false; + def isTerm = false; + def isType = false; + def isEmpty = false; + + override def toString(): String = { + val buffer = new StringWriter(); + val printer = treePrinters.create(new PrintWriter(buffer)); + printer.print(this); printer.flush; + buffer.toString() + } + + override def hashCode(): int = super.hashCode(); + + override def equals(that: Any): boolean = that match { + case t: Tree => this eq t + case _ => false + } + + def duplicate: this.type = (duplicator transform this).asInstanceOf[this.type]; + + def copyAttrs(tree: Tree): this.type = { + posx = tree.posx; + tpe = tree.tpe; + if (hasSymbol) symbol = tree.symbol; + this + } + } + + [_trait_] abstract class SymTree extends Tree { + override def hasSymbol = true; + override var symbol: Symbol = NoSymbol; + } + + abstract class DefTree extends SymTree { + def name: Name; + override def isDef = true; + } + + trait TermTree extends Tree { + override def isTerm = true + } + + trait TypTree extends Tree { + override def isType = true + } + +// ----- auxiliary objects and methods ------------------------------ + + private val duplicator = new Transformer { + override val copy = new StrictTreeCopier; + } + + private def syntheticParams(owner: Symbol, formals: List[Type]): List[Symbol] = { + var cnt = 0; + def freshName() = { cnt = cnt + 1; newTermName("x$" + cnt) } + for (val formal <- formals) yield + owner.newValueParameter(owner.pos, freshName()).setInfo(formal); + } + + private def syntheticParams(owner: Symbol, mtp: Type): List[List[Symbol]] = mtp match { + case PolyType(_, restp) => + syntheticParams(owner, restp) + case MethodType(formals, restp) => + syntheticParams(owner, formals) :: syntheticParams(owner, restp) + case _ => + List() + } + +// def nextPhase = if (phase.id > globalPhase.id) phase else phase.next; + +// ----- tree node alternatives -------------------------------------- + + /** The empty tree */ + case object EmptyTree extends TermTree { + tpe = NoType; + override def isEmpty = true; + } + + abstract class MemberDef extends DefTree { + def mods: Modifiers; + def name: Name; + def keyword : String; + final def hasFlag(mask: long): boolean = (mods.flags & mask) != 0; + + def namePos(source : SourceFile) : Int = if (pos == Position.NOPOS) Position.NOPOS else { + assert(keyword != null); + if (!source.beginsWith(pos, keyword + " ")) { + val p = new Position(source, pos); + // System.err.println("SYM=" + symbol + " KW=" + keyword + " LINE=" + p.lineContent + " TEXT=" + source.content(pos) + source.content(pos + 1)); + if (true) return Position.NOPOS; + else throw new Error(); + } + source.skipWhitespace(pos + keyword.length() + 1); + } + } + + /** Package clause */ + case class PackageDef(name: Name, stats: List[Tree]) + extends MemberDef { + def mods = NoMods; + def keyword = "package"; + } + + def PackageDef(sym: Symbol, stats: List[Tree]): PackageDef = + PackageDef(sym.name, stats) setSymbol sym; + + + abstract class ImplDef extends MemberDef { + def impl: Template + } + + /** Class definition */ + case class ClassDef(mods: Modifiers, name: Name, tparams: List[AbsTypeDef], tpt: Tree, impl: Template) + extends ImplDef { + def keyword = "class"; + } + + def ClassDef(sym: Symbol, impl: Template): ClassDef = + posAssigner.atPos(sym.pos) { + ClassDef(Modifiers(sym.flags), + sym.name, + sym.typeParams map AbsTypeDef, + if (sym.thisSym == sym) EmptyTree else TypeTree(sym.typeOfThis), + impl) setSymbol sym + } + + /** Construct class definition with given class symbol, value parameters, supercall arguments + * and template body. + * @param sym the class symbol + * @param vparamss the value parameters -- if they have symbols they should be owned by `sym' + * @param argss the supercall arguments + * @param body the template statements without primary constructor and value parameter fields. + */ + def ClassDef(sym: Symbol, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree]): ClassDef = + ClassDef(sym, Template(sym.info.parents map TypeTree, vparamss, argss, body)); + + /** Singleton object definition */ + case class ModuleDef(mods: Modifiers, name: Name, impl: Template) + extends ImplDef { + def keyword = "object"; + } + + def ModuleDef(sym: Symbol, impl: Template): ModuleDef = + posAssigner.atPos(sym.pos) { + ModuleDef(Modifiers(sym.flags), sym.name, impl) + } + + + abstract class ValOrDefDef extends MemberDef { + def tpt: Tree; + def rhs: Tree; + } + + /** Value definition */ + case class ValDef(mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) + extends ValOrDefDef { + assert(tpt.isType, tpt); + assert(rhs.isTerm, rhs); + def keyword = if (mods.isVariable) "var" else "val"; + override def namePos(source : SourceFile) = + if (pos == Position.NOPOS) Position.NOPOS; + else if (source.beginsWith(pos, "val ") || source.beginsWith(pos, "var ")) source.skipWhitespace(pos + ("val ").length()); + else if (source.content(pos) == ',') source.skipWhitespace(pos + 1); + else pos; + } + + + def ValDef(sym: Symbol, rhs: Tree): ValDef = { + posAssigner.atPos(sym.pos) { + ValDef(Modifiers(sym.flags), sym.name, TypeTree(sym.tpe), rhs) setSymbol sym + } + } + + def ValDef(sym: Symbol): ValDef = ValDef(sym, EmptyTree); + + /** Method definition */ + case class DefDef(mods: Modifiers, name: Name, tparams: List[AbsTypeDef], + vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) + extends ValOrDefDef { + assert(tpt.isType); + assert(rhs.isTerm); + def keyword = "def"; + } + + def DefDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef = + posAssigner.atPos(sym.pos) { + assert(sym != NoSymbol); + DefDef(Modifiers(sym.flags), + sym.name, + sym.typeParams map AbsTypeDef, + vparamss, + TypeTree(sym.tpe.finalResultType), + rhs) setSymbol sym + } + + def DefDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef = { + val vparamss = syntheticParams(sym, sym.tpe); + DefDef(sym, vparamss map (.map(ValDef)), rhs(vparamss)); + } + + /** Abstract type or type parameter */ + case class AbsTypeDef(mods: Modifiers, name: Name, lo: Tree, hi: Tree) + extends DefTree { + def keyword = ""; + + override def setPos(pos : Int) : this.type = { + val ret = super.setPos(pos); + ret; + } + def namePos = pos - name.length; + } + + def AbsTypeDef(sym: Symbol): AbsTypeDef = + posAssigner.atPos(sym.pos) { + AbsTypeDef(Modifiers(sym.flags), sym.name, + TypeTree(sym.info.bounds.lo), TypeTree(sym.info.bounds.hi)) + } + + /** Type alias */ + case class AliasTypeDef(mods: Modifiers, name: Name, tparams: List[AbsTypeDef], rhs: Tree) + extends MemberDef { + def keyword = "type"; + } + + def AliasTypeDef(sym: Symbol, rhs: Tree): AliasTypeDef = + posAssigner.atPos(sym.pos) { + AliasTypeDef(Modifiers(sym.flags), sym.name, sym.typeParams map AbsTypeDef, rhs) + } + + /** Labelled expression - the symbols in the array (must be Idents!) + * are those the label takes as argument + * + * The symbol that is given to the labeldef should have a MethodType + * (as if it were a nested function) + * + * jumps are apply nodes attributed with label symbol, the arguements + * will get assigned to the idents. + * + * note: on 2005-06-09 Martin, Iuli, Burak agreed to have forward + * jumps within a Block. + */ + case class LabelDef(name: Name, params: List[Ident], rhs: Tree) + extends DefTree with TermTree { + assert(rhs.isTerm); + } + + def LabelDef(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef = + posAssigner.atPos(sym.pos) { + LabelDef(sym.name, params map Ident, rhs) setSymbol sym + } + + /** Import clause */ + case class Import(expr: Tree, selectors: List[Pair[Name, Name]]) + extends SymTree; + + /** Attribuetd definition */ + case class Attributed(attribute: Tree, definition: Tree) + extends Tree { + override def symbol: Symbol = definition.symbol; + override def symbol_=(sym: Symbol): unit = { definition.symbol = sym } + } + + /** Documented definition, eliminated by analyzer */ + case class DocDef(comment: String, definition: Tree) + extends Tree { + override def symbol: Symbol = definition.symbol; + override def symbol_=(sym: Symbol): unit = { definition.symbol = sym } + } + + /** Instantiation template */ + case class Template(parents: List[Tree], body: List[Tree]) + extends SymTree { + // System.err.println("TEMPLATE: " + parents); + } + + def Template(parents: List[Tree], vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree]): Template = { + /** Add constructor to template */ + var vparamss1 = + vparamss map (.map (vd => + ValDef(Modifiers(vd.mods.flags & IMPLICIT | PARAM), vd.name, vd.tpt.duplicate, EmptyTree))); + if (vparamss1.isEmpty || + !vparamss1.head.isEmpty && (vparamss1.head.head.mods.flags & IMPLICIT) != 0) + vparamss1 = List() :: vparamss1; + val superRef: Tree = Select(Super(nme.EMPTY.toTypeName, nme.EMPTY.toTypeName), nme.CONSTRUCTOR); + val superCall = (superRef /: argss) (Apply); + val constr: Tree = DefDef(NoMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), superCall); + Template(parents, List.flatten(vparamss) ::: constr :: body) + } + + /** Block of expressions (semicolon separated expressions) */ + case class Block(stats: List[Tree], expr: Tree) + extends TermTree; + + /** Case clause in a pattern match, eliminated by TransMatch + * (except for occurences in switch statements) + */ + case class CaseDef(pat: Tree, guard: Tree, body: Tree) + extends Tree; + + /** casedef shorthand */ + def CaseDef(pat: Tree, body: Tree): CaseDef = CaseDef(pat, EmptyTree, body); + + /** Sequence of patterns (comma separated expressions), eliminated by TransMatch */ + case class Sequence(trees: List[Tree]) + extends TermTree; + + /** Alternatives of patterns, eliminated by TransMatch, except for + * occurences in encoded Switch stmt (=remaining Match(CaseDef(...)) + */ + case class Alternative(trees: List[Tree]) + extends TermTree; + + /** Repetition of pattern, eliminated by TransMatch */ + case class Star(elem: Tree) + extends TermTree; + + /** Bind of a variable to a rhs pattern, eliminated by TransMatch */ + case class Bind(name: Name, body: Tree) + extends DefTree; + + def Bind(sym: Symbol, body: Tree): Bind = + Bind(sym.name, body) setSymbol sym; + + /** Array of expressions, needs to be translated in backend, + */ + case class ArrayValue(elemtpt: Tree, elems: List[Tree]) + extends TermTree; + + /** Anonymous function, eliminated by analyzer */ + case class Function(vparams: List[ValDef], body: Tree) + extends TermTree with SymTree; + + /** Assignment */ + case class Assign(lhs: Tree, rhs: Tree) + extends TermTree; + + /** Conditional expression */ + case class If(cond: Tree, thenp: Tree, elsep: Tree) + extends TermTree; + + /** Pattern matching expression (before TransMatch) + * Switch statements (after TransMatch) + * + * after TM, cases will satisfy the following constraints: + * - all guards are EmptyTree, + * - all patterns will be either Literal(Constant(x:Int)) or Alternative(lit|...|lit) + * - except for an "otherwise" branch, which has pattern Ident(nme.WILDCARD) + */ + case class Match(selector: Tree, cases: List[CaseDef]) extends TermTree; + + /** Return expression */ + case class Return(expr: Tree) + extends TermTree with SymTree; + + case class Try(block: Tree, catches: List[CaseDef], finalizer: Tree) + extends TermTree; + + /** Throw expression */ + case class Throw(expr: Tree) + extends TermTree; + + /** Object instantiation + * @param tpt a class type + * one should always use factory method below to build a user level new. + */ + case class New(tpt: Tree) + extends TermTree { + assert(tpt.isType) + } + + /** Factory method for object creation <new tpt(args_1)...(args_n)> */ + def New(tpt: Tree, argss: List[List[Tree]]): Tree = { + assert(!argss.isEmpty); + val superRef: Tree = Select(New(tpt), nme.CONSTRUCTOR); + (superRef /: argss) (Apply); + } + + /** Type annotation, eliminated by explicit outer */ + case class Typed(expr: Tree, tpt: Tree) + extends TermTree; + + abstract class GenericApply(val fun0: Tree, val args0: List[Tree]) extends TermTree; + + + /** Type application */ + case class TypeApply(fun: Tree, args: List[Tree]) + extends GenericApply(fun, args) { + override def symbol: Symbol = fun.symbol; + override def symbol_=(sym: Symbol): unit = { fun.symbol = sym } + } + + /** Value application */ + case class Apply(fun: Tree, args: List[Tree]) + extends GenericApply(fun, args) { + override def symbol: Symbol = fun.symbol; + override def symbol_=(sym: Symbol): unit = { fun.symbol = sym } + } + + /** Super reference */ + case class Super(qual: Name, mixin: Name) + extends TermTree with SymTree; + + def Super(sym: Symbol, mixin: Name): Tree = Super(sym.name, mixin) setSymbol sym; + + /** Self reference */ + case class This(qual: Name) + extends TermTree with SymTree; + + def This(sym: Symbol): Tree = This(sym.name) setSymbol sym; + + /** Designator */ + case class Select(qualifier: Tree, selector: Name) + extends SymTree { + override def isTerm = selector.isTermName; + override def isType = selector.isTypeName; + + override def setPos(pos : Int) : this.type = { + val ret = super.setPos(pos); + // System.err.println("SELECT_POS: " + pos + " " + this); + // if (pos == 74) Thread.dumpStack(); + ret; + } + + + } + + def Select(qualifier: Tree, sym: Symbol): Select = + Select(qualifier, sym.name) setSymbol sym; + + /** Identifier */ + case class Ident(name: Name) + extends SymTree { + override def isTerm = name.isTermName; + override def isType = name.isTypeName; + + override def setPos(p : Int) : this.type = { + val ret = super.setPos(p); + ret; + } + + } + + def Ident(sym: Symbol): Ident = + Ident(sym.name) setSymbol sym; + + /** Literal */ + case class Literal(value: Constant) + extends TermTree { + assert(value != null) + } + + def Literal(value: Any): Literal = + Literal(Constant(value)); + + /** General type term, introduced by RefCheck. */ + case class TypeTree() extends TypTree { + var original : Tree = _; + + def setOriginal(tree : Tree) : this.type = { + tree match { + case tt : TypeTree => + System.err.println("Illegal: " + this + " to " + tree); + Thread.dumpStack(); + case _ => + } + + original = tree; + setPos(tree.pos); + } + override def setPos(pos : Int) : this.type = { + val ret = super.setPos(pos); + if (false && pos == 151 && original == null) { + System.err.println("TYPE: " + this + " POS=" + pos + " TPE=" + tpe); + Thread.dumpStack(); + } + ret; + } + + + override def isEmpty = tpe == null || tpe == NoType; + } + + def TypeTree(tp: Type): TypeTree = TypeTree() setType tp; + // def TypeTree(tp: Type, tree : Tree): TypeTree = TypeTree(tree) setType tp; + + + /** Singleton type, eliminated by RefCheck */ + case class SingletonTypeTree(ref: Tree) + extends TypTree; + + /** Type selection, eliminated by RefCheck */ + case class SelectFromTypeTree(qualifier: Tree, selector: Name) + extends TypTree with SymTree; + + /** Intersection type, eliminated by RefCheck */ + case class CompoundTypeTree(templ: Template) + extends TypTree; + + /** Applied type, eliminated by RefCheck */ + case class AppliedTypeTree(tpt: Tree, args: List[Tree]) + extends TypTree { + override def symbol: Symbol = tpt.symbol; + override def symbol_=(sym: Symbol): unit = { tpt.symbol = sym } + } + +/* A standard pattern match + case EmptyTree => + case PackageDef(name, stats) => + case ClassDef(mods, name, tparams, tpt, impl) => + case ModuleDef(mods, name, impl) => (eliminated by refcheck) + case ValDef(mods, name, tpt, rhs) => + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case AbsTypeDef(mods, name, lo, hi) => (eliminated by erasure) + case AliasTypeDef(mods, name, tparams, rhs) => (eliminated by erasure) + case LabelDef(name, params, rhs) => + case Import(expr, selectors) => (eliminated by typecheck) + case Attributed(attribute, definition) => (eliminated by typecheck) + case DocDef(comment, definition) => (eliminated by typecheck) + case Template(parents, body) => + case Block(stats, expr) => + case CaseDef(pat, guard, body) => (eliminated by transmatch) + case Sequence(trees) => (eliminated by transmatch) + case Alternative(trees) => (eliminated by transmatch) + case Star(elem) => (eliminated by transmatch) + case Bind(name, body) => (eliminated by transmatch) + case ArrayValue(elemtpt, trees) => (introduced by uncurry) + case Function(vparams, body) => (eliminated by typecheck) + case Assign(lhs, rhs) => + case If(cond, thenp, elsep) => + case Match(selector, cases) => + case Return(expr) => + case Try(block, catches, finalizer) => + case Throw(expr) => + case New(tpt) => + case Typed(expr, tpt) => (eliminated by erasure) + case TypeApply(fun, args) => + case Apply(fun, args) => + case Super(qual, mixin) => + case This(qual) => + case Select(qualifier, selector) => + case Ident(name) => + case Literal(value) => + case TypeTree() => + case SingletonTypeTree(ref) => (eliminated by typecheck) + case SelectFromTypeTree(qualifier, selector) => (eliminated by typecheck) + case CompoundTypeTree(templ: Template) => (eliminated by typecheck) + case AppliedTypeTree(tpt, args) => (eliminated by typecheck) +*/ + + trait TreeCopier { + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], tpt: Tree, impl: Template): ClassDef; + def PackageDef(tree: Tree, name: Name, stats: List[Tree]): PackageDef; + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template): ModuleDef; + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree): ValDef; + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef; + def AbsTypeDef(tree: Tree, mods: Modifiers, name: Name, lo: Tree, hi: Tree): AbsTypeDef; + def AliasTypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], rhs: Tree): AliasTypeDef; + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree): LabelDef; + def Import(tree: Tree, expr: Tree, selectors: List[Pair[Name, Name]]): Import; + def Attributed(tree: Tree, attribute: Tree, definition: Tree): Attributed; + def DocDef(tree: Tree, comment: String, definition: Tree): DocDef; + def Template(tree: Tree, parents: List[Tree], body: List[Tree]): Template; + def Block(tree: Tree, stats: List[Tree], expr: Tree): Block; + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree): CaseDef; + def Sequence(tree: Tree, trees: List[Tree]): Sequence; + def Alternative(tree: Tree, trees: List[Tree]): Alternative; + def Star(tree: Tree, elem: Tree): Star; + def Bind(tree: Tree, name: Name, body: Tree): Bind; + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]): ArrayValue; + def Function(tree: Tree, vparams: List[ValDef], body: Tree): Function; + def Assign(tree: Tree, lhs: Tree, rhs: Tree): Assign; + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree): If; + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]): Match; + def Return(tree: Tree, expr: Tree): Return; + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree): Try; + def Throw(tree: Tree, expr: Tree): Throw; + def New(tree: Tree, tpt: Tree): New; + def Typed(tree: Tree, expr: Tree, tpt: Tree): Typed; + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]): TypeApply; + def Apply(tree: Tree, fun: Tree, args: List[Tree]): Apply; + def Super(tree: Tree, qual: Name, mixin: Name): Super; + def This(tree: Tree, qual: Name): This; + def Select(tree: Tree, qualifier: Tree, selector: Name): Select; + def Ident(tree: Tree, name: Name): Ident; + def Literal(tree: Tree, value: Constant): Literal; + def TypeTree(tree: Tree): TypeTree; + def SingletonTypeTree(tree: Tree, ref: Tree): SingletonTypeTree; + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name): SelectFromTypeTree; + def CompoundTypeTree(tree: Tree, templ: Template): CompoundTypeTree; + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]): AppliedTypeTree; + } + + class StrictTreeCopier extends TreeCopier { + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], tpt: Tree, impl: Template) = + new ClassDef(mods, name, tparams, tpt, impl).copyAttrs(tree); + def PackageDef(tree: Tree, name: Name, stats: List[Tree]) = + new PackageDef(name, stats).copyAttrs(tree); + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template) = + new ModuleDef(mods, name, impl).copyAttrs(tree); + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) = + new ValDef(mods, name, tpt, rhs).copyAttrs(tree); + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) = + new DefDef(mods, name, tparams, vparamss, tpt, rhs).copyAttrs(tree); + def AbsTypeDef(tree: Tree, mods: Modifiers, name: Name, lo: Tree, hi: Tree) = + new AbsTypeDef(mods, name, lo, hi).copyAttrs(tree); + def AliasTypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], rhs: Tree) = + new AliasTypeDef(mods, name, tparams, rhs).copyAttrs(tree); + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree) = + new LabelDef(name, params, rhs).copyAttrs(tree); + def Import(tree: Tree, expr: Tree, selectors: List[Pair[Name, Name]]) = + new Import(expr, selectors).copyAttrs(tree); + def Attributed(tree: Tree, attribute: Tree, definition: Tree) = + new Attributed(attribute, definition).copyAttrs(tree); + def DocDef(tree: Tree, comment: String, definition: Tree) = + new DocDef(comment, definition).copyAttrs(tree); + def Template(tree: Tree, parents: List[Tree], body: List[Tree]) = + new Template(parents, body).copyAttrs(tree); + def Block(tree: Tree, stats: List[Tree], expr: Tree) = + new Block(stats, expr).copyAttrs(tree); + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree) = + new CaseDef(pat, guard, body).copyAttrs(tree); + def Sequence(tree: Tree, trees: List[Tree]) = + new Sequence(trees).copyAttrs(tree); + def Alternative(tree: Tree, trees: List[Tree]) = + new Alternative(trees).copyAttrs(tree); + def Star(tree: Tree, elem: Tree) = + new Star(elem).copyAttrs(tree); + def Bind(tree: Tree, name: Name, body: Tree) = + new Bind(name, body).copyAttrs(tree); + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]) = + new ArrayValue(elemtpt, trees).copyAttrs(tree); + def Function(tree: Tree, vparams: List[ValDef], body: Tree) = + new Function(vparams, body).copyAttrs(tree); + def Assign(tree: Tree, lhs: Tree, rhs: Tree) = + new Assign(lhs, rhs).copyAttrs(tree); + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree) = + new If(cond, thenp, elsep).copyAttrs(tree); + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]) = + new Match(selector, cases).copyAttrs(tree); + def Return(tree: Tree, expr: Tree) = + new Return(expr).copyAttrs(tree); + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree) = + new Try(block, catches, finalizer).copyAttrs(tree); + def Throw(tree: Tree, expr: Tree) = + new Throw(expr).copyAttrs(tree); + def New(tree: Tree, tpt: Tree) = + new New(tpt).copyAttrs(tree); + def Typed(tree: Tree, expr: Tree, tpt: Tree) = + new Typed(expr, tpt).copyAttrs(tree); + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]) = + new TypeApply(fun, args).copyAttrs(tree); + def Apply(tree: Tree, fun: Tree, args: List[Tree]) = + new Apply(fun, args).copyAttrs(tree); + def Super(tree: Tree, qual: Name, mixin: Name) = + new Super(qual, mixin).copyAttrs(tree); + def This(tree: Tree, qual: Name) = + new This(qual).copyAttrs(tree); + def Select(tree: Tree, qualifier: Tree, selector: Name) = + new Select(qualifier, selector).copyAttrs(tree); + def Ident(tree: Tree, name: Name) = + new Ident(name).copyAttrs(tree); + def Literal(tree: Tree, value: Constant) = + new Literal(value).copyAttrs(tree); + def TypeTree(tree: Tree) = + new TypeTree().copyAttrs(tree); + def SingletonTypeTree(tree: Tree, ref: Tree) = + new SingletonTypeTree(ref).copyAttrs(tree); + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name) = + new SelectFromTypeTree(qualifier, selector).copyAttrs(tree); + def CompoundTypeTree(tree: Tree, templ: Template) = + new CompoundTypeTree(templ).copyAttrs(tree); + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]) = + new AppliedTypeTree(tpt, args).copyAttrs(tree) + } + + class LazyTreeCopier(copy: TreeCopier) extends TreeCopier { + def this() = this(new StrictTreeCopier); + def ClassDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], tpt: Tree, impl: Template) = tree match { + case t @ ClassDef(mods0, name0, tparams0, tpt0, impl0) + if (mods0 == mods && (name0 == name) && (tparams0 == tparams) && (tpt0 == tpt) && (impl0 == impl)) => t + case _ => copy.ClassDef(tree, mods, name, tparams, tpt, impl) + } + def PackageDef(tree: Tree, name: Name, stats: List[Tree]) = tree match { + case t @ PackageDef(name0, stats0) + if ((name0 == name) && (stats0 == stats)) => t + case _ => copy.PackageDef(tree, name, stats) + } + def ModuleDef(tree: Tree, mods: Modifiers, name: Name, impl: Template) = tree match { + case t @ ModuleDef(mods0, name0, impl0) + if (mods0 == mods && (name0 == name) && (impl0 == impl)) => t + case _ => copy.ModuleDef(tree, mods, name, impl) + } + def ValDef(tree: Tree, mods: Modifiers, name: Name, tpt: Tree, rhs: Tree) = tree match { + case t @ ValDef(mods0, name0, tpt0, rhs0) + if (mods0 == mods && (name0 == name) && (tpt0 == tpt) && (rhs0 == rhs)) => t + case _ => copy.ValDef(tree, mods, name, tpt, rhs) + } + def DefDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) = tree match { + case t @ DefDef(mods0, name0, tparams0, vparamss0, tpt0, rhs0) + if (mods0 == mods && (name0 == name) && (tparams0 == tparams) && (vparamss0 == vparamss) && (tpt0 == tpt) && (rhs == rhs0)) => t + case _ => copy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs) + } + def AbsTypeDef(tree: Tree, mods: Modifiers, name: Name, lo: Tree, hi: Tree) = tree match { + case t @ AbsTypeDef(mods0, name0, lo0, hi0) + if (mods0 == mods && (name0 == name) && (lo0 == lo) && (hi0 == hi)) => t + case _ => copy.AbsTypeDef(tree, mods, name, lo, hi) + } + def AliasTypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[AbsTypeDef], rhs: Tree) = tree match { + case t @ AliasTypeDef(mods0, name0, tparams0, rhs0) + if (mods0 == mods && (name0 == name) && (tparams0 == tparams) && (rhs0 == rhs)) => t + case _ => copy.AliasTypeDef(tree, mods, name, tparams, rhs) + } + def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree) = tree match { + case t @ LabelDef(name0, params0, rhs0) + if ((name0 == name) && (params0 == params) && (rhs0 == rhs)) => t + case _ => copy.LabelDef(tree, name, params, rhs) + } + def Import(tree: Tree, expr: Tree, selectors: List[Pair[Name, Name]]) = tree match { + case t @ Import(expr0, selectors0) + if ((expr0 == expr) && (selectors0 == selectors)) => t + case _ => copy.Import(tree, expr, selectors) + } + def Attributed(tree: Tree, attribute: Tree, definition: Tree) = tree match { + case t @ Attributed(attribute0, definition0) + if ((attribute0 == attribute) && (definition0 == definition)) => t + case _ => copy.Attributed(tree, attribute, definition) + } + def DocDef(tree: Tree, comment: String, definition: Tree) = tree match { + case t @ DocDef(comment0, definition0) + if ((comment0 == comment) && (definition0 == definition)) => t + case _ => copy.DocDef(tree, comment, definition) + } + def Template(tree: Tree, parents: List[Tree], body: List[Tree]) = tree match { + case t @ Template(parents0, body0) + if ((parents0 == parents) && (body0 == body)) => t + case _ => copy.Template(tree, parents, body) + } + def Block(tree: Tree, stats: List[Tree], expr: Tree) = tree match { + case t @ Block(stats0, expr0) + if ((stats0 == stats) && (expr0 == expr)) => t + case _ => copy.Block(tree, stats, expr) + } + def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree) = tree match { + case t @ CaseDef(pat0, guard0, body0) + if ((pat0 == pat) && (guard0 == guard) && (body0 == body)) => t + case _ => copy.CaseDef(tree, pat, guard, body) + } + def Sequence(tree: Tree, trees: List[Tree]) = tree match { + case t @ Sequence(trees0) + if ((trees0 == trees)) => t + case _ => copy.Sequence(tree, trees) + } + def Alternative(tree: Tree, trees: List[Tree]) = tree match { + case t @ Alternative(trees0) + if ((trees0 == trees)) => t + case _ => copy.Alternative(tree, trees) + } + def Star(tree: Tree, elem: Tree) = tree match { + case t @ Star(elem0) + if ((elem0 == elem)) => t + case _ => copy.Star(tree, elem) + } + def Bind(tree: Tree, name: Name, body: Tree) = tree match { + case t @ Bind(name0, body0) + if ((name0 == name) && (body0 == body)) => t + case _ => copy.Bind(tree, name, body) + } + def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]) = tree match { + case t @ ArrayValue(elemtpt0, trees0) + if ((elemtpt0 == elemtpt) && (trees0 == trees)) => t + case _ => copy.ArrayValue(tree, elemtpt, trees) + } + def Function(tree: Tree, vparams: List[ValDef], body: Tree) = tree match { + case t @ Function(vparams0, body0) + if ((vparams0 == vparams) && (body0 == body)) => t + case _ => copy.Function(tree, vparams, body) + } + def Assign(tree: Tree, lhs: Tree, rhs: Tree) = tree match { + case t @ Assign(lhs0, rhs0) + if ((lhs0 == lhs) && (rhs0 == rhs)) => t + case _ => copy.Assign(tree, lhs, rhs) + } + def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree) = tree match { + case t @ If(cond0, thenp0, elsep0) + if ((cond0 == cond) && (thenp0 == thenp) && (elsep0 == elsep)) => t + case _ => copy.If(tree, cond, thenp, elsep) + } + def Match(tree: Tree, selector: Tree, cases: List[CaseDef]) = tree match { + case t @ Match(selector0, cases0) + if ((selector0 == selector) && (cases0 == cases)) => t + case _ => copy.Match(tree, selector, cases) + } + def Return(tree: Tree, expr: Tree) = tree match { + case t @ Return(expr0) + if ((expr0 == expr)) => t + case _ => copy.Return(tree, expr) + } + def Try(tree: Tree, block: Tree, catches: List[CaseDef], finalizer: Tree) = tree match { + case t @ Try(block0, catches0, finalizer0) + if ((block0 == block) && (catches0 == catches) && (finalizer0 == finalizer)) => t + case _ => copy.Try(tree, block, catches, finalizer) + } + def Throw(tree: Tree, expr: Tree) = tree match { + case t @ Throw(expr0) + if ((expr0 == expr)) => t + case _ => copy.Throw(tree, expr) + } + def New(tree: Tree, tpt: Tree) = tree match { + case t @ New(tpt0) + if ((tpt0 == tpt)) => t + case _ => copy.New(tree, tpt) + } + def Typed(tree: Tree, expr: Tree, tpt: Tree) = tree match { + case t @ Typed(expr0, tpt0) + if ((expr0 == expr) && (tpt0 == tpt)) => t + case _ => copy.Typed(tree, expr, tpt) + } + def TypeApply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { + case t @ TypeApply(fun0, args0) + if ((fun0 == fun) && (args0 == args)) => t + case _ => copy.TypeApply(tree, fun, args) + } + def Apply(tree: Tree, fun: Tree, args: List[Tree]) = tree match { + case t @ Apply(fun0, args0) + if ((fun0 == fun) && (args0 == args)) => t + case _ => copy.Apply(tree, fun, args) + } + def Super(tree: Tree, qual: Name, mixin: Name) = tree match { + case t @ Super(qual0, mixin0) + if ((qual0 == qual) && (mixin0 == mixin)) => t + case _ => copy.Super(tree, qual, mixin) + } + def This(tree: Tree, qual: Name) = tree match { + case t @ This(qual0) + if ((qual0 == qual)) => t + case _ => copy.This(tree, qual) + } + def Select(tree: Tree, qualifier: Tree, selector: Name) = tree match { + case t @ Select(qualifier0, selector0) + if ((qualifier0 == qualifier) && (selector0 == selector)) => t + case _ => copy.Select(tree, qualifier, selector) + } + def Ident(tree: Tree, name: Name) = tree match { + case t @ Ident(name0) + if ((name0 == name)) => t + case _ => copy.Ident(tree, name) + } + def Literal(tree: Tree, value: Constant) = tree match { + case t @ Literal(value0) + if (value0 == value) => t + case _ => copy.Literal(tree, value) + } + def TypeTree(tree: Tree) = tree match { + case t @ TypeTree() => t + case _ => copy.TypeTree(tree) + } + def SingletonTypeTree(tree: Tree, ref: Tree) = tree match { + case t @ SingletonTypeTree(ref0) + if ((ref0 == ref)) => t + case _ => copy.SingletonTypeTree(tree, ref) + } + def SelectFromTypeTree(tree: Tree, qualifier: Tree, selector: Name) = tree match { + case t @ SelectFromTypeTree(qualifier0, selector0) + if ((qualifier0 == qualifier) && (selector0 == selector)) => t + case _ => copy.SelectFromTypeTree(tree, qualifier, selector) + } + def CompoundTypeTree(tree: Tree, templ: Template) = tree match { + case t @ CompoundTypeTree(templ0) + if (templ0 == templ) => t + case _ => copy.CompoundTypeTree(tree, templ) + } + def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]) = tree match { + case t @ AppliedTypeTree(tpt0, args0) + if ((tpt0 == tpt) && (args0 == args)) => t + case _ => copy.AppliedTypeTree(tree, tpt, args) + } + } + + abstract class Transformer { + val copy: TreeCopier = new LazyTreeCopier; + protected var currentOwner: Symbol = definitions.RootClass; + def transform(tree: Tree): Tree = tree match { + case EmptyTree => + tree + case PackageDef(name, stats) => + atOwner(tree.symbol.moduleClass) { + copy.PackageDef(tree, name, transformStats(stats, currentOwner)) + } + case ClassDef(mods, name, tparams, tpt, impl) => + atOwner(tree.symbol) { + copy.ClassDef(tree, mods, name, transformAbsTypeDefs(tparams), transform(tpt), transformTemplate(impl)) + } + case ModuleDef(mods, name, impl) => + atOwner(tree.symbol.moduleClass) { + copy.ModuleDef(tree, mods, name, transformTemplate(impl)) + } + case ValDef(mods, name, tpt, rhs) => + atOwner(tree.symbol) { + copy.ValDef(tree, mods, name, transform(tpt), transform(rhs)) + } + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + atOwner(tree.symbol) { + copy.DefDef( + tree, mods, name, transformAbsTypeDefs(tparams), transformValDefss(vparamss), transform(tpt), transform(rhs)) + } + case AbsTypeDef(mods, name, lo, hi) => + atOwner(tree.symbol) { + copy.AbsTypeDef(tree, mods, name, transform(lo), transform(hi)) + } + case AliasTypeDef(mods, name, tparams, rhs) => + atOwner(tree.symbol) { + copy.AliasTypeDef(tree, mods, name, transformAbsTypeDefs(tparams), transform(rhs)) + } + case LabelDef(name, params, rhs) => + copy.LabelDef(tree, name, transformIdents(params), transform(rhs)) //bq: Martin, once, atOwner(...) works, also change `LamdaLifter.proxy' + case Import(expr, selectors) => + copy.Import(tree, transform(expr), selectors) + case Attributed(attribute, definition) => + copy.Attributed(tree, transform(attribute), transform(definition)) + case DocDef(comment, definition) => + copy.DocDef(tree, comment, transform(definition)) + case Template(parents, body) => + copy.Template(tree, transformTrees(parents), transformStats(body, tree.symbol)) + case Block(stats, expr) => + copy.Block(tree, transformStats(stats, currentOwner), transform(expr)) + case CaseDef(pat, guard, body) => + copy.CaseDef(tree, transform(pat), transform(guard), transform(body)) + case Sequence(trees) => + copy.Sequence(tree, transformTrees(trees)) + case Alternative(trees) => + copy.Alternative(tree, transformTrees(trees)) + case Star(elem) => + copy.Star(tree, transform(elem)) + case Bind(name, body) => + copy.Bind(tree, name, transform(body)) + case ArrayValue(elemtpt, trees) => + copy.ArrayValue(tree, transform(elemtpt), transformTrees(trees)) + case Function(vparams, body) => + copy.Function(tree, transformValDefs(vparams), transform(body)) + case Assign(lhs, rhs) => + copy.Assign(tree, transform(lhs), transform(rhs)) + case If(cond, thenp, elsep) => + copy.If(tree, transform(cond), transform(thenp), transform(elsep)) + case Match(selector, cases) => + copy.Match(tree, transform(selector), transformCaseDefs(cases)) + case Return(expr) => + copy.Return(tree, transform(expr)) + case Try(block, catches, finalizer) => + copy.Try(tree, transform(block), transformCaseDefs(catches), transform(finalizer)) + case Throw(expr) => + copy.Throw(tree, transform(expr)) + case New(tpt) => + copy.New(tree, transform(tpt)) + case Typed(expr, tpt) => + copy.Typed(tree, transform(expr), transform(tpt)) + case TypeApply(fun, args) => + copy.TypeApply(tree, transform(fun), transformTrees(args)) + case Apply(fun, args) => + copy.Apply(tree, transform(fun), transformTrees(args)) + case Super(qual, mixin) => + copy.Super(tree, qual, mixin) + case This(qual) => + copy.This(tree, qual) + case Select(qualifier, selector) => + copy.Select(tree, transform(qualifier), selector) + case Ident(name) => + copy.Ident(tree, name) + case Literal(value) => + copy.Literal(tree, value) + case TypeTree() => + copy.TypeTree(tree) + case SingletonTypeTree(ref) => + copy.SingletonTypeTree(tree, transform(ref)) + case SelectFromTypeTree(qualifier, selector) => + copy.SelectFromTypeTree(tree, transform(qualifier), selector) + case CompoundTypeTree(templ) => + copy.CompoundTypeTree(tree, transformTemplate(templ)) + case AppliedTypeTree(tpt, args) => + copy.AppliedTypeTree(tree, transform(tpt), transformTrees(args)) + } + + def transformTrees(trees: List[Tree]): List[Tree] = + List.mapConserve(trees)(transform); + def transformTemplate(tree: Template): Template = + transform(tree: Tree).asInstanceOf[Template]; + def transformAbsTypeDefs(trees: List[AbsTypeDef]): List[AbsTypeDef] = + List.mapConserve(trees)(tree => transform(tree).asInstanceOf[AbsTypeDef]); + def transformValDefs(trees: List[ValDef]): List[ValDef] = + List.mapConserve(trees)(tree => transform(tree).asInstanceOf[ValDef]); + def transformValDefss(treess: List[List[ValDef]]): List[List[ValDef]] = + List.mapConserve(treess)(tree => transformValDefs(tree)); + def transformCaseDefs(trees: List[CaseDef]): List[CaseDef] = + List.mapConserve(trees)(tree => transform(tree).asInstanceOf[CaseDef]); + def transformIdents(trees: List[Ident]): List[Ident] = + List.mapConserve(trees)(tree => transform(tree).asInstanceOf[Ident]); + def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = + List.mapConserve(stats)(stat => + if (exprOwner != currentOwner && stat.isTerm) atOwner(exprOwner)(transform(stat)) + else transform(stat)) filter (EmptyTree !=); + def transformUnit(unit: CompilationUnit): unit = { unit.body = transform(unit.body) } + + def atOwner[A](owner: Symbol)(trans: => A): A = { + val prevOwner = currentOwner; + currentOwner = owner; + val result = trans; + currentOwner = prevOwner; + result + } + } + + class Traverser { + protected var currentOwner: Symbol = definitions.RootClass; + def traverse(tree: Tree): unit = tree match { + case EmptyTree => + ; + case PackageDef(name, stats) => + atOwner(tree.symbol.moduleClass) { + traverseTrees(stats) + } + case ClassDef(mods, name, tparams, tpt, impl) => + atOwner(tree.symbol) { + traverseTrees(tparams); traverse(tpt); traverse(impl) + } + case ModuleDef(mods, name, impl) => + atOwner(tree.symbol.moduleClass) { + traverse(impl) + } + case ValDef(mods, name, tpt, rhs) => + atOwner(tree.symbol) { + traverse(tpt); traverse(rhs) + } + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + atOwner(tree.symbol) { + traverseTrees(tparams); traverseTreess(vparamss); traverse(tpt); traverse(rhs) + } + case AbsTypeDef(mods, name, lo, hi) => + atOwner(tree.symbol) { + traverse(lo); traverse(hi); + } + case AliasTypeDef(mods, name, tparams, rhs) => + atOwner(tree.symbol) { + traverseTrees(tparams); traverse(rhs) + } + case LabelDef(name, params, rhs) => + traverseTrees(params); traverse(rhs) + case Import(expr, selectors) => + traverse(expr) + case Attributed(attribute, definition) => + traverse(attribute); traverse(definition) + case DocDef(comment, definition) => + traverse(definition) + case Template(parents, body) => + traverseTrees(parents); traverseStats(body, tree.symbol) + case Block(stats, expr) => + traverseTrees(stats); traverse(expr) + case CaseDef(pat, guard, body) => + traverse(pat); traverse(guard); traverse(body) + case Sequence(trees) => + traverseTrees(trees) + case Alternative(trees) => + traverseTrees(trees) + case Star(elem) => + traverse(elem) + case Bind(name, body) => + traverse(body) + case ArrayValue(elemtpt, trees) => + traverse(elemtpt); traverseTrees(trees) + case Function(vparams, body) => + traverseTrees(vparams); traverse(body) + case Assign(lhs, rhs) => + traverse(lhs); traverse(rhs) + case If(cond, thenp, elsep) => + traverse(cond); traverse(thenp); traverse(elsep) + case Match(selector, cases) => + traverse(selector); traverseTrees(cases) + case Return(expr) => + traverse(expr) + case Try(block, catches, finalizer) => + traverse(block); traverseTrees(catches); traverse(finalizer) + case Throw(expr) => + traverse(expr) + case New(tpt) => + traverse(tpt) + case Typed(expr, tpt) => + traverse(expr); traverse(tpt) + case TypeApply(fun, args) => + traverse(fun); traverseTrees(args) + case Apply(fun, args) => + traverse(fun); traverseTrees(args) + case Super(_, _) => + ; + case This(_) => + ; + case Select(qualifier, selector) => + traverse(qualifier) + case Ident(_) => + ; + case Literal(_) => + ; + case TypeTree() => + ; + case SingletonTypeTree(ref) => + traverse(ref) + case SelectFromTypeTree(qualifier, selector) => + traverse(qualifier) + case CompoundTypeTree(templ) => + traverse(templ) + case AppliedTypeTree(tpt, args) => + traverse(tpt); traverseTrees(args) + } + + def traverseTrees(trees: List[Tree]): unit = + trees foreach traverse; + def traverseTreess(treess: List[List[Tree]]): unit = + treess foreach traverseTrees; + def traverseStats(stats: List[Tree], exprOwner: Symbol): unit = + stats foreach (stat => + if (exprOwner != currentOwner && stat.isTerm) atOwner(exprOwner)(traverse(stat)) + else traverse(stat)); + def apply[T <: Tree](tree: T): T = { traverse(tree); tree } + + def atOwner(owner: Symbol)(traverse: => unit): unit = { + val prevOwner = currentOwner; + currentOwner = owner; + traverse; + currentOwner = prevOwner; + } + } + + class TreeSubstituter(from: List[Symbol], to: List[Tree]) extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Ident(_) => + def subst(from: List[Symbol], to: List[Tree]): Tree = + if (from.isEmpty) tree + else if (tree.symbol == from.head) to.head + else subst(from.tail, to.tail); + subst(from, to) + case _ => + super.transform(tree) + } + } + + class TreeTypeSubstituter(from: List[Symbol], to: List[Type]) extends Traverser { + val typeSubst = new SubstTypeMap(from, to); + override def traverse(tree: Tree): unit = { + if (tree.tpe != null) tree.tpe = typeSubst(tree.tpe); + super.traverse(tree) + } + override def apply[T <: Tree](tree: T): T = super.apply(tree.duplicate) + } + + class TreeSymSubstituter(from: List[Symbol], to: List[Symbol]) extends Traverser { + val symSubst = new SubstSymMap(from, to); + override def traverse(tree: Tree): unit = { + def subst(from: List[Symbol], to: List[Symbol]): unit = { + if (!from.isEmpty) + if (tree.symbol == from.head) tree setSymbol to.head + else subst(from.tail, to.tail) + } + if (tree.tpe != null) tree.tpe = symSubst(tree.tpe); + if (tree.hasSymbol) subst(from, to); + super.traverse(tree) + } + override def apply[T <: Tree](tree: T): T = super.apply(tree.duplicate) + } + + class ChangeOwnerTraverser(val oldowner: Symbol, val newowner: Symbol) extends Traverser { + override def traverse(tree: Tree): unit = { + if ((tree.isDef || tree.isInstanceOf[Function]) && tree.symbol != NoSymbol && tree.symbol.owner == oldowner) + tree.symbol.owner = newowner; + super.traverse(tree) + } + } + + final class TreeList { + private var trees = List[Tree](); + def append(t: Tree): TreeList = { trees = t :: trees; this } + def append(ts: List[Tree]): TreeList = { trees = ts reverse_::: trees; this } + def toList: List[Tree] = trees.reverse; + } + + object posAssigner extends Traverser { + private var pos: int = _; + override def traverse(t: Tree): unit = + if (t != EmptyTree && t.pos == Position.NOPOS) { + t.setPos(pos); + super.traverse(t); + } + def atPos[T <: Tree](pos: int)(tree: T): T = { + this.pos = pos; + traverse(tree); + tree + } + } +} + diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala new file mode 100644 index 0000000000..cf4244cb3f --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala @@ -0,0 +1,583 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +//import java.lang.{Integer, Long, Float, Double}; + +//import scalac._; +//import scalac.ast._; +//import scalac.atree.AConstant; +//import scalac.symtab.Modifiers; + +import scala.Iterator; +import scala.collection.immutable.ListMap ; +import scala.collection.mutable; +//import scala.tools.scalac.util.NewArray; +import scala.tools.nsc.util.Position; +import scala.xml.{Text,TextBuffer}; + + +trait MarkupParsers: SyntaxAnalyzer { + + import global._ ; + import posAssigner.atPos; + +class MarkupParser(unit: CompilationUnit, s: Scanner, p: Parser, presWS: boolean) /*with scala.xml.parsing.MarkupParser[Tree,Tree] */{ + + import Tokens.{EMPTY, LBRACE, RBRACE}; + + final val preserveWS = presWS; + + import p.{symbXMLBuilder => handle}; + import s.token; + + /** the XML tree factory */ + //final val handle: SymbolicXMLBuilder = p.symbXMLBuilder; + //new SymbolicXMLBuilder(unit.global.make, unit.global.treeGen, p, presWS); + + /** holds the position in the source file */ + /*[Duplicate]*/ var pos: Int = _; + + /** holds temporary values of pos */ + /*[Duplicate]*/ var tmppos: Int = _; + + /** holds the next character */ + /*[Duplicate]*/ var ch: Char = _; + + /** character buffer, for names */ + /*[Duplicate]*/ protected val cbuf = new StringBuffer(); + + /** append Unicode character to name buffer*/ + /*[Duplicate]*/ protected def putChar(c: char) = cbuf.append( c ); + + /*[Duplicate]*/ var xEmbeddedBlock = false; + + /** munch expected XML token, report syntax error for unexpected */ + /*[Duplicate]*/ def xToken(that: Char): Unit = { + if( ch == that ) + nextch; + else + reportSyntaxError("'" + that + "' expected instead of '" + ch + "'"); + } + + var debugLastStartElement = new mutable.Stack[Pair[Int,String]]; + + /** checks whether next character starts a Scala block, if yes, skip it. + * @return true if next character starts a scala block + */ + /*[Duplicate]*/ def xCheckEmbeddedBlock:Boolean = { + xEmbeddedBlock = + enableEmbeddedExpressions && ( ch == '{' ) && { nextch;( ch != '{' ) }; + return xEmbeddedBlock; + } + + /** parse attribute and add it to listmap + * [41] Attributes ::= { S Name Eq AttValue } + * AttValue ::= `'` { _ } `'` + * | `"` { _ } `"` + * | `{` scalablock `}` + */ + /*[Duplicate]*/ def xAttributes = { + var aMap = new mutable.HashMap[String, Tree](); + while (xml.Parsing.isNameStart(ch)) { + val key = xName; + xEQ; + val delim = ch; + val pos1 = pos; + val value: /* AttribValue[*/Tree/*]*/ = ch match { + case '"' | '\'' => + nextch; + val tmp = xAttributeValue( delim ); + nextch; + Literal(Constant(tmp)); + case '{' if enableEmbeddedExpressions => + nextch; + xEmbeddedExpr; + case _ => + reportSyntaxError( "' or \" delimited attribute value or '{' scala-expr '}' expected" ); + Literal(Constant("<syntax-error>")) + }; + // well-formedness constraint: unique attribute names + if( aMap.contains( key )) + reportSyntaxError( "attribute "+key+" may only be defined once" ); + aMap.update( key, value ); + if(( ch != '/' )&&( ch != '>' )) + xSpace; + }; + aMap + } + + /** attribute value, terminated by either ' or ". value may not contain <. + * @param endch either ' or " + */ + /*[Duplicate]*/ def xAttributeValue(endCh: char): String = { + while (ch != endCh) { + putChar(ch); + nextch; + }; + val str = cbuf.toString(); + cbuf.setLength(0); + // @todo: normalize attribute value + // well-formedness constraint + if (str.indexOf('<') != -1) { + reportSyntaxError( "'<' not allowed in attrib value" ); "" + } else { + str + } + } + + /** parse a start or empty tag. + * [40] STag ::= '<' Name { S Attribute } [S] + * [44] EmptyElemTag ::= '<' Name { S Attribute } [S] + */ + /*[Duplicate]*/ def xTag: Pair[String, mutable.Map[String, Tree]] = { + val elemName = xName; + xSpaceOpt; + val aMap = if (xml.Parsing.isNameStart(ch)) { + xAttributes; + } else { + new mutable.HashMap[String, Tree](); + } + Tuple2( elemName, aMap ); + } + + /* [42] '<' xmlEndTag ::= '<' '/' Name S? '>' */ + /*[Duplicate]*/ def xEndTag(n: String) = { + xToken('/'); + val m = xName; + if(n != m) reportSyntaxError( "expected closing tag of " + n/* +", not "+m*/); + xSpaceOpt; + xToken('>') + } + + /** '<! CharData ::= [CDATA[ ( {char} - {char}"]]>"{char} ) ']]>' + * + * see [15] + */ + /*[Duplicate]*/ def xCharData: Tree = { + xToken('['); + xToken('C'); + xToken('D'); + xToken('A'); + xToken('T'); + xToken('A'); + xToken('['); + val pos1 = pos; + val sb:StringBuffer = new StringBuffer(); + while (true) { + if (ch==']' && + { sb.append( ch ); nextch; ch == ']' } && + { sb.append( ch ); nextch; ch == '>' } ) { + sb.setLength( sb.length() - 2 ); + nextch; + return handle.charData( pos1, sb.toString() ); + } else sb.append( ch ); + nextch; + } + Predef.error("this cannot happen"); + }; + + /** CharRef ::= "&#" '0'..'9' {'0'..'9'} ";" + * | "&#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";" + * + * see [66] + */ + /*[Duplicate]*/ def xCharRef:String = { + val hex = ( ch == 'x' ) && { nextch; true }; + val base = if (hex) 16 else 10; + var i = 0; + while (ch != ';') { + ch match { + case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => + i = i * base + Character.digit( ch, base ); + case 'a' | 'b' | 'c' | 'd' | 'e' | 'f' + | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' => + if( !hex ) + reportSyntaxError("hex char not allowed in decimal char ref\n" + +"Did you mean to write &#x ?"); + else + i = i * base + Character.digit( ch, base ); + case _ => + reportSyntaxError("character '"+ch+" not allowed in char ref\n"); + } + nextch; + } + new String(Predef.Array(i.asInstanceOf[char])) + } +/** Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' + * + * see [15] + */ + /*[Duplicate]*/ def xComment: Tree = { + val sb:StringBuffer = new StringBuffer(); + xToken('-'); + xToken('-'); + while (true) { + if( ch=='-' && { sb.append( ch ); nextch; ch == '-' } ) { + sb.setLength( sb.length() - 1 ); + nextch; + xToken('>'); + return handle.comment( pos, sb.toString() ); + } else sb.append( ch ); + nextch; + } + Predef.error("this cannot happen"); + }; + + /*[Duplicate]*/ def appendText(pos: int, ts:mutable.Buffer[Tree], txt:String):Unit = { + if( !preserveWS ) + for( val t <- TextBuffer.fromString( txt ).toText ) { + ts.append( handle.text( pos, t.text ) ); + } + else + ts.append( handle.text( pos, txt )); + } + + /*[Duplicate]*/ def content: mutable.Buffer[Tree] = { + var ts = new mutable.ArrayBuffer[Tree]; + var exit = false; + while( !exit ) { + if( xEmbeddedBlock ) { + ts.append( xEmbeddedExpr ); + } else { + tmppos = pos; + ch match { + case '<' => // another tag + nextch; + ch match { + case '/' => + exit = true; // end tag + case '!' => + nextch; + if( '[' == ch ) // CDATA + ts.append( xCharData ); + else // comment + ts.append( xComment ); + case '?' => // PI + nextch; + ts.append( xProcInstr ); + case _ => + ts.append( element ); // child + } + + case '{' => + if( xCheckEmbeddedBlock ) { + ts.append(xEmbeddedExpr); + } else { + val str = new StringBuffer("{"); + str.append( xText ); + appendText(tmppos, ts, str.toString()); + } + // postcond: xEmbeddedBlock == false! + case '&' => // EntityRef or CharRef + nextch; + ch match { + case '#' => // CharacterRef + nextch; + val theChar = handle.text( tmppos, xCharRef ); + xToken(';'); + ts.append( theChar ); + case _ => // EntityRef + val n = xName ; + xToken(';'); + ts.append( handle.entityRef( tmppos, n ) ); + } + case _ => // text content + appendText(tmppos, ts, xText); + // here xEmbeddedBlock might be true + } + } + } + ts + } /* end content */ + + /** '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag + * | xmlTag1 '/' '>' + */ + /*[Duplicate]*/ def element: Tree = { + val pos1 = pos; + val Tuple2(qname, attrMap) = xTag; + //Console.println("MarkupParser::element("+qname+","+attrMap+")"); + if (ch == '/') { // empty element + xToken('/'); + xToken('>'); + handle.element( pos1, qname, attrMap, new mutable.ListBuffer[Tree] ); + } + else { // handle content + xToken('>'); + debugLastStartElement.push(Pair(pos1,qname)); + val ts = content; + xEndTag( qname ); + debugLastStartElement.pop; + handle.element( pos1, qname, attrMap, ts ); + } + } + + + /** Name ::= (Letter | '_' | ':') (NameChar)* + * + * see [5] of XML 1.0 specification + */ + /*[Duplicate]*/ def xName: String = { + if( xml.Parsing.isNameStart( ch ) ) { + do { + putChar( ch ); + nextch; + } while( xml.Parsing.isNameChar( ch ) ); + val n = cbuf.toString().intern(); + cbuf.setLength( 0 ); + n + } else { + reportSyntaxError( "name expected, but char '"+ch+"' cannot start a name" ); + new String(); + } + } + + + /** scan [S] '=' [S]*/ + /*[Duplicate]*/ def xEQ = { xSpaceOpt; xToken('='); xSpaceOpt } + + /** skip optional space S? */ + /*[Duplicate]*/ def xSpaceOpt = { while( xml.Parsing.isSpace( ch ) ) { nextch; }} + + /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */ + /*[Duplicate]*/ def xSpace = { + if (xml.Parsing.isSpace(ch)) { + nextch; xSpaceOpt + } + else { + reportSyntaxError("whitespace expected"); + } + } + +/** '<?' ProcInstr ::= Name [S ({Char} - ({Char}'>?' {Char})]'?>' + * + * see [15] + */ + /*[Duplicate]*/ def xProcInstr: Tree = { + val sb:StringBuffer = new StringBuffer(); + val n = xName; + if( xml.Parsing.isSpace( ch ) ) { + xSpace; + while( true ) { + if( ch=='?' && { sb.append( ch ); nextch; ch == '>' } ) { + sb.setLength( sb.length() - 1 ); + nextch; + return handle.procInstr(tmppos, n.toString(), sb.toString()); + } else + sb.append( ch ); + nextch; + } + }; + xToken('?'); + xToken('>'); + return handle.procInstr(tmppos, n.toString(), sb.toString()); + } + + /** parse character data. + * precondition: xEmbeddedBlock == false (we are not in a scala block) + */ + /*[Duplicate]*/ def xText: String = { + if( xEmbeddedBlock ) Predef.error("internal error: encountered embedded block"); // assert + + if( xCheckEmbeddedBlock ) + return "" + else { + var exit = false; + while( !exit ) { + putChar( ch ); + exit = { nextch; xCheckEmbeddedBlock }||( ch == '<' ) || ( ch == '&' ); + } + val str = cbuf.toString(); + cbuf.setLength( 0 ); + str + } + } + //override type Tree = handle.Tree; + //override type Tree = handle.Tree; + + final val PATTERN = true; + final val EXPR = false; + + val enableEmbeddedExpressions: Boolean = true; + + //val cbuf = new StringBuffer(); + + /** append Unicode character to name buffer*/ + //private def putChar(c: char) = cbuf.append( c ); + + /** xLiteral = element { element } + * @return Scala representation of this xml literal + * precondition: s.xStartsXML == true + */ + def xLiteral: Tree = try { + init; + handle.isPattern = false; + val pos = s.currentPos; + var tree = element; + xSpaceOpt; + // parse more XML ? + if (ch == '<') { + val ts = new mutable.ArrayBuffer[Tree](); + ts.append( tree ); + while( ch == '<' ) { + nextch; + //Console.println("DEBUG 1: I am getting char '"+ch+"'"); // DEBUG + ts.append( element ); + xSpaceOpt; + } + tree = handle.makeXMLseq( pos, ts ); + } + //Console.println("out of xLiteral, parsed:"+tree.toString()); + s.next.token = EMPTY; + s.nextToken(); /* s.fetchToken(); */ + tree + } + catch { + case _:ArrayIndexOutOfBoundsException => + s.syntaxError(debugLastStartElement.top._1, + "missing end tag in XML literal for <" + +debugLastStartElement.top._2+">"); + EmptyTree; + } + + /** @see xmlPattern. resynchronizes after succesful parse + * @return this xml pattern + * precondition: s.xStartsXML == true + */ + def xLiteralPattern:Tree = try { + init; + val oldMode = handle.isPattern; + handle.isPattern = true; + val pos = s.currentPos; + var tree = xPattern; xSpaceOpt; + //if (ch == '<') { + var ts: List[Tree] = List(); + ts = tree :: ts; + + s.next.token = EMPTY; s.nextToken(); /* ?????????? */ + while( token == Tokens.XMLSTART ) {// ??????????????????????????? + //while (ch == '<' /* && lookahead != '-'*/) { + nextch; + //Console.println("DEBUG 2: I am getting char '"+ch+"'"); // DEBUG + ts = xPattern :: ts; + //xSpaceOpt; // ???? + s.next.token = EMPTY; s.nextToken(); /* ?????????? */ + //Console.println("DEBUG 3: resync'ed, token = '"+s+"'"); // DEBUG + } + //Console.println("current token == "+s); + tree = handle.makeXMLseqPat( pos, ts.reverse ); + //} + handle.isPattern = oldMode; + //Console.println("out of xLiteralPattern, parsed:"+tree.toString()); + // s.next.token = EMPTY; // ?? + // s.nextToken(); /* s.fetchToken(); */ // ?? + tree + }catch { + case _:ArrayIndexOutOfBoundsException => + s.syntaxError(debugLastStartElement.top._1, + "missing end tag in XML literal for <" + +debugLastStartElement.top._2+">"); + EmptyTree; + } + + def xEmbeddedExpr:Tree = { + sync; + val b = p.expr(true,false); + if(/*s.*/token != RBRACE) + reportSyntaxError(" expected end of Scala block"); + init; + //Console.println("[out of xScalaExpr s.ch = "+s.ch+" ch="+ch+"]"); + return b + } + + /** xScalaPatterns ::= patterns + */ + def xScalaPatterns: List[Tree] = { + sync; + val b = p.patterns(); + if (/*s.*/token != RBRACE) + reportSyntaxError(" expected end of Scala patterns"); + init; + return b + } + + //var ch: Char = _; + + /** this method assign the next character to ch and advances in input */ + def nextch: Unit = { s.in.next; /*s.xNext;*/ ch = s.in.ch ; pos = s.currentPos; } + + //def lookahead = { s.xLookahead } + + def init: Unit = { + ch = s.in.ch; + pos = s.currentPos; + //Console.println("\ninit! ch = "+ch); + } + + def reportSyntaxError(str: String) = { + s.syntaxError("in XML literal: " + str); + nextch; + } + + def sync: Unit = { + xEmbeddedBlock = false; + s.xSync; + } + + /** '<' xPattern ::= Name [S] { xmlPattern | '{' pattern3 '}' } ETag + * | Name [S] '/' '>' + */ + def xPattern:Tree = { + //Console.println("xPattern"); + val pos1 = pos; + val qname = xName; + debugLastStartElement.push(Pair(pos1,qname)); + xSpaceOpt; + if( ch == '/' ) { // empty tag + nextch; + xToken('>'); + return handle.makeXMLpat( pos1, qname, new mutable.ArrayBuffer[Tree]() ); + }; + + // else: tag with content + xToken('>'); + var ts = new mutable.ArrayBuffer[Tree]; + var exit = false; + while (! exit) { + val pos2 = pos; + if( xEmbeddedBlock ) { + ts ++ xScalaPatterns; + } else + ch match { + case '<' => { // tag + nextch; + if( ch != '/' ) { //child + ts.append( xPattern ); + } else { + exit = true + } + } + case '{' => // embedded Scala patterns + while( ch == '{' ) { + s.in.next; + ts ++ xScalaPatterns; + } + // postcond: xEmbeddedBlock = false; + if (xEmbeddedBlock) Predef.error("problem with embedded block"); // assert + case _ => // teMaxt + appendText( pos2, ts, xText ); + // here xEmbeddedBlock might be true; + //if( xEmbeddedBlock ) throw new ApplicationError("after:"+text); // assert + } + } + xEndTag(qname); + debugLastStartElement.pop; + handle.makeXMLpat(pos1, qname, ts); + } + +} /* class MarkupParser */ +} diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala new file mode 100644 index 0000000000..0634ace4d0 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -0,0 +1,1808 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +import scala.tools.nsc.util.Position; +import util.ListBuffer; +import symtab.Flags; +import Tokens._; + +/** Performs the following context-free rewritings: + * (1) Places all pattern variables in Bind nodes. In a pattern, for identifiers `x': + * x => x @ _ + * x:T => x @ (_ : T) + * + * (2) Removes pattern definitions (PatDef's) as follows: + * If pattern is a simple (typed) identifier: + * val x = e ==> val x = e + * val x: T = e ==> val x: T = e + * + * if there are no variables in pattern + * val p = e ==> e.match (case p => ()) + * + * if there is exactly one variable in pattern + * val x_1 = e.match (case p => (x_1)) + * + * if there is more than one variable in pattern + * val p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) + * val x_1 = t$._1 + * ... + * val x_N = t$._N + * + * (3) Removes function types as follows: + * (argtpes) => restpe ==> scala.Function_n[argtpes, restpe] + * + * (4) Wraps naked case definitions in a match as follows: + * { cases } ==> (x => x.match {cases}), except when already argument to match + */ +[_trait_] abstract class Parsers: SyntaxAnalyzer { + + import global._; + import posAssigner.atPos; + + class Parser(unit: CompilationUnit) { + + val in = new Scanner(unit); + + /** the markup parser */ + val xmlp = new MarkupParser(unit, in, Parser.this, true); + + object treeBuilder extends TreeBuilder { + val global: Parsers.this.global.type = Parsers.this.global; + def freshName(prefix: String): Name = unit.fresh.newName(prefix); + } + import treeBuilder._; + + object symbXMLBuilder extends SymbolicXMLBuilder(treeBuilder, Parser.this, true) { // DEBUG choices + val global: Parsers.this.global.type = Parsers.this.global; + def freshName(prefix: String): Name = unit.fresh.newName(prefix); + } + + /** this is the general parse method + */ + def parse(): Tree = { + val t = compilationUnit(); + accept(EOF); + t + } + +/////// ERROR HANDLING ////////////////////////////////////////////////////// + + private def skip(): unit = { + //System.out.println("<skipping> " + in.token2string(in.token));//DEBUG + var nparens = 0; + var nbraces = 0; + while (true) { + in.token match { + case EOF => + return; + case SEMI => + if (nparens == 0 && nbraces == 0) return; + case NEWLINE => + if (nparens == 0 && nbraces == 0) return; + case RPAREN => + nparens = nparens - 1; + case RBRACE => + if (nbraces == 0) return; + nbraces = nbraces - 1; + case LPAREN => + nparens = nparens + 1; + case LBRACE => + nbraces = nbraces + 1; + case _ => + } + in.nextToken(); + } + } + + def syntaxError(msg: String, skipIt: boolean): unit = + syntaxError(in.currentPos, msg, skipIt); + + def syntaxError(pos: int, msg: String, skipIt: boolean): unit = { + if (pos != in.errpos) { + unit.error(pos, msg); + in.errpos = pos; + } + if (skipIt) skip(); + } + + def accept(token: int): int = { + val pos = in.currentPos; + if (in.token != token) + syntaxError( + if (Position.line(unit.source, in.currentPos) > Position.line(unit.source, in.lastPos)) in.lastPos + else in.currentPos, + in.token2string(token) + " expected but " + + in.token2string(in.token) + " found.", true); + if (in.token == token) in.nextToken(); + pos; + } + + /** SEP = NL | `;' + * NL = `\n' // where allowed + */ + def acceptStatSep(): unit = if (in.token == NEWLINE) in.nextToken() else accept(SEMI); + + def errorTypeTree = TypeTree().setType(ErrorType).setPos(in.currentPos); + def errorTermTree = Literal(Constant(null)).setPos(in.currentPos); + def errorPatternTree = Ident(nme.WILDCARD).setPos(in.currentPos); + +/////// TOKEN CLASSES ////////////////////////////////////////////////////// + + def isModifier: boolean = in.token match { + case ABSTRACT | FINAL | SEALED | PRIVATE | PROTECTED | OVERRIDE | IMPLICIT => true + case _ => false + } + + def isLocalModifier: boolean = in.token match { + case ABSTRACT | FINAL | SEALED => true + case _ => false + } + + def isDefIntro: boolean = in.token match { + case VAL | VAR | DEF | TYPE | OBJECT | + CASEOBJECT | CLASS | CASECLASS | TRAIT => true + case _ => false + } + + def isDclIntro: boolean = in.token match { + case VAL | VAR | DEF | TYPE => true + case _ => false + } + + def isExprIntro: boolean = in.token match { + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | + STRINGLIT | SYMBOLLIT | TRUE | FALSE | NULL | IDENTIFIER | + THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE | + DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true + case _ => false + } + +/////// COMMENT AND ATTRIBUTE COLLECTION ////////////////////////////////////// + + /** Join the comment associated with a definition + */ + def joinComment(trees: => List[Tree]): List[Tree] = { + val buf = in.docBuffer; + if (buf != null) { + in.docBuffer = null; + trees map (t => DocDef(buf.toString(), t) setPos t.pos) + } else trees + } + +/////// TREE CONSTRUCTION //////////////////////////////////////////////////// + + def scalaDot(name: Name): Tree = + Select(Ident(nme.scala_), name); + def scalaAnyRefConstr: Tree = + scalaDot(nme.AnyRef.toTypeName); + def scalaScalaObjectConstr: Tree = + scalaDot(nme.ScalaObject.toTypeName); + def caseClassConstr: Tree = + scalaDot(nme.CaseClass.toTypeName); + + /** Convert tree to formal parameter list + */ + def convertToParams(t: Tree): List[ValDef] = t match { + case Function(params, TypeTree()) => + params + case Ident(_) | Typed(Ident(_), _) => + List(convertToParam(t)); + case Literal(c) if c.tag == UnitTag => + Nil + case _ => + syntaxError(t.pos, "malformed formal parameter list", false); + Nil + } + + /** Convert tree to formal parameter + */ + def convertToParam(tree: Tree): ValDef = + atPos(tree.pos) { + tree match { + case Ident(name) => + ValDef(Modifiers(Flags.PARAM), name, TypeTree(), EmptyTree) + case Typed(Ident(name), tpe) => + ValDef(Modifiers(Flags.PARAM), name, tpe, EmptyTree) + case _ => + syntaxError(tree.pos, "not a legal formal parameter", false); + ValDef(Modifiers(Flags.PARAM), nme.ERROR, errorTypeTree, EmptyTree) + } + } + + /** Convert (qual)ident to type identifier + */ + def convertToTypeId(tree: Tree): Tree = tree match { + case Ident(name) => + Ident(name.toTypeName).setPos(tree.pos) + case Select(qual, name) => + Select(qual, name.toTypeName).setPos(tree.pos) + case _ => + System.out.println(tree);//debug + syntaxError(tree.pos, "identifier expected", false); + errorTypeTree + } + + /** make closure from tree */ + def makeClosure(tree: Tree): Tree = { + val pname: Name = unit.fresh.newName("x$"); + def insertParam(tree: Tree): Tree = tree match { + case Ident(name) => + Select(Ident(pname), name) + case Select(qual, name) => + Select(insertParam(qual), name) + case Apply(fn, args) => + Apply(insertParam(fn), args) + case TypeApply(fn, args) => + TypeApply(insertParam(fn), args) + case _ => + syntaxError(tree.pos, "cannot convert to closure", false); + errorTermTree + } + Function( + List(ValDef(Modifiers(Flags.PARAM), pname, TypeTree(), EmptyTree)), + insertParam(tree)) + } + +/////// OPERAND/OPERATOR STACK ///////////////////////////////////////////////// + + case class OpInfo(operand: Tree, operator: Name, pos: int); + var opstack: List[OpInfo] = Nil; + + def precedence(operator: Name): int = + if (operator eq nme.ERROR) -1 + else { + val firstCh = operator(0); + if (((firstCh >= 'A') && (firstCh <= 'Z')) || + ((firstCh >= 'a') && (firstCh <= 'z'))) + 1 + else + firstCh match { + case '|' => 2 + case '^' => 3 + case '&' => 4 + case '<' | '>' => 5 + case '=' | '!' => 6 + case ':' => 7 + case '+' | '-' => 8; + case '*' | '/' | '%' => 9; + case _ => 10; + } + } + + def reduceStack(isExpr: boolean, base: List[OpInfo], top0: Tree, prec: int, leftAssoc: boolean): Tree = { + var top = top0; + if (opstack != base && + precedence(opstack.head.operator) == prec && + treeInfo.isLeftAssoc(opstack.head.operator) != leftAssoc) { + syntaxError( + opstack.head.pos, + "left- and right-associative operators with same precedence may not be mixed", + false); + } + while (opstack != base && + (prec < precedence(opstack.head.operator) || + (leftAssoc && prec == precedence(opstack.head.operator)))) { + top = atPos(opstack.head.pos) { + makeBinop(isExpr, opstack.head.operand, opstack.head.operator, top) + } + opstack = opstack.tail; + } + top + } + +/////// IDENTIFIERS AND LITERALS //////////////////////////////////////////////////////////// + + final val MINUS: Name = "-"; + final val PLUS : Name = "+"; + final val BANG : Name = "!"; + final val TILDE: Name = "~"; + final val STAR : Name = "*"; + final val BAR : Name = "|"; + final val OPT : Name = "?"; + final val LT : Name = "<"; + + def ident(): Name = + if (in.token == IDENTIFIER) { + val name = in.name.encode; + in.nextToken(); + name + } else { + accept(IDENTIFIER); + nme.ERROR + } + + /** StableRef ::= StableId + * | [Ident `.'] this + * SimpleType ::= StableRef [`.' type] + */ + def stableRef(thisOK: boolean, typeOK: boolean): Tree = { + var t: Tree = null; + if (in.token == THIS) { + t = atPos(in.skipToken()) { This(nme.EMPTY.toTypeName) } + if (!thisOK || in.token == DOT) + t = { selectors(t, typeOK, accept(DOT)) } + } else if (in.token == SUPER) { + t = atPos(in.skipToken()) { + Super(nme.EMPTY.toTypeName, mixinQualifierOpt()) + } + t = atPos(accept(DOT)) { Select(t, ident()) } + if (in.token == DOT) + t = { selectors(t, typeOK, in.skipToken()) } + } else { + val i = atPos(in.currentPos) { Ident(ident()) } + t = i; + if (in.token == DOT) { + val pos = in.skipToken(); + if (in.token == THIS) { + in.nextToken(); + t = atPos(i.pos) { This(i.name.toTypeName) } + if (!thisOK || in.token == DOT) + t = { selectors(t, typeOK, accept(DOT)) } + } else if (in.token == SUPER) { + in.nextToken(); + t = atPos(i.pos) { Super(i.name.toTypeName, mixinQualifierOpt()) } + t = atPos(accept(DOT)) { Select(t, ident())} + if (in.token == DOT) + t = { selectors(t, typeOK, in.skipToken()) } + } else { + t = { selectors(t, typeOK, pos) } + } + } + } + t + } + + def selectors(t: Tree, typeOK: boolean, pos : Int): Tree = + if (typeOK && in.token == TYPE) { + in.nextToken(); + atPos(pos) { SingletonTypeTree(t) } + } else { + val t1 = atPos(pos) { Select(t, ident()); } + if (in.token == DOT) { selectors(t1, typeOK, in.skipToken()) } + else t1 + } + + /** MixinQualifier ::= `[' Id `]' + */ + def mixinQualifierOpt(): Name = + if (in.token == LBRACKET) { + in.nextToken(); + val name = ident().toTypeName; + accept(RBRACKET); + name + } else { + nme.EMPTY.toTypeName + } + + /** StableId ::= Id + * | StableRef `.' Id + * | [Id '.'] super [MixinQualifier] ` `.' Id + */ + def stableId(): Tree = + stableRef(false, false); + + /** QualId ::= Id {`.' Id} + */ + def qualId(): Tree = { + val id = atPos(in.currentPos) { Ident(ident()) } + if (in.token == DOT) { selectors(id, false, in.skipToken()) } + else id + } + + /** SimpleExpr ::= literal + * | symbol [ArgumentExprs] + * | null + */ + def literal(isPattern: boolean, isNegated: boolean): Tree = { + def litToTree() = atPos(in.currentPos) { + Literal( + in.token match { + case CHARLIT => + Constant(in.intVal.asInstanceOf[char]) + case INTLIT => + Constant(in.intVal(isNegated).asInstanceOf[int]) + case LONGLIT => + Constant(in.intVal(isNegated)) + case FLOATLIT => + Constant(in.floatVal(isNegated).asInstanceOf[float]) + case DOUBLELIT => + Constant(in.floatVal(isNegated)) + case STRINGLIT | SYMBOLLIT => + Constant(in.name.toString()) + case TRUE => + Constant(true) + case FALSE => + Constant(false) + case NULL => + Constant(null) + case _ => + syntaxError("illegal literal", true); + null + }) + } + + val isSymLit = in.token == SYMBOLLIT; + val t = litToTree(); + val pos = in.skipToken(); + if (isSymLit) { + atPos(pos) { + var symid = scalaDot(nme.Symbol); + if (isPattern) { symid = convertToTypeId(symid) } + Apply(symid, List(t)) + } + } else { + t + } + } + + def newLineOpt(): unit = if (in.token == NEWLINE) in.nextToken(); + +//////// TYPES /////////////////////////////////////////////////////////////// + + /** TypedOpt ::= [`:' Type] + */ + def typedOpt(): Tree = + if (in.token == COLON) { in.nextToken(); typ() } + else TypeTree(); + + /** RequiresTypedOpt ::= [`:' SimpleType | requires SimpleType] + */ + def requiresTypeOpt(): Tree = + if (in.token == COLON | in.token == REQUIRES) { in.nextToken(); simpleType() } + else TypeTree(); + + /** Types ::= Type {`,' Type} + */ + def types(): List[Tree] = { + val ts = new ListBuffer[Tree] + typ(); + while (in.token == COMMA) { + in.nextToken(); + ts += typ(); + } + ts.toList + } + + /** Type ::= Type1 `=>' Type + * | `(' [Types] `)' `=>' Type + * | Type1 + */ + def typ(): Tree = { + val t = + if (in.token == LPAREN) { + in.nextToken(); + if (in.token == RPAREN) { + in.nextToken(); + atPos(accept(ARROW)) { makeFunctionTypeTree(List(), typ()) } + } else { + val t0 = typ(); + if (in.token == COMMA) { + in.nextToken(); + val ts = new ListBuffer[Tree] + t0 ++ types(); + accept(RPAREN); + atPos (accept(ARROW)) { makeFunctionTypeTree(ts.toList, typ()) } + } else { + accept(RPAREN); t0 + } + } + } else { + type1() + } + if (in.token == ARROW) atPos(in.skipToken()) { + makeFunctionTypeTree(List(t), typ()) } + else t + } + + /** Type1 ::= SimpleType {with SimpleType} [Refinement] + */ + def type1(): Tree = { + val pos = in.currentPos; + var ts = new ListBuffer[Tree] + simpleType(); + while (in.token == WITH) { + in.nextToken(); ts += simpleType() + } + atPos(pos) { + if (in.token == LBRACE) CompoundTypeTree(Template(ts.toList, refinement())) + else makeIntersectionTypeTree(ts.toList) + } + } + + /** SimpleType ::= SimpleType TypeArgs + * | SimpleType `#' Id + * | StableId + * | StableRef `.' type + * | `(' Type `)' + */ + def simpleType(): Tree = { + val pos = in.currentPos; + var t: Tree = + if (in.token == LPAREN) { + in.nextToken(); + val t = typ(); + accept(RPAREN); + t + } else { + val r = stableRef(false, true); + val x = r match { + case SingletonTypeTree(_) => r + case _ => convertToTypeId(r); + } + // System.err.println("SIMPLE_TYPE: " + r.pos + " " + r + " => " + x.pos + " " + x); + x; + } + while (true) { + if (in.token == HASH) + t = atPos(in.skipToken()) { + SelectFromTypeTree(t, ident().toTypeName); + } + else if (in.token == LBRACKET) + t = atPos(pos) { AppliedTypeTree(t, typeArgs()) } + else + return t + } + null; //dummy + } + + /** TypeArgs ::= `[' Types `]' + */ + def typeArgs(): List[Tree] = { + accept(LBRACKET); + val ts = types(); + accept(RBRACKET); + ts + } + +//////// EXPRESSIONS //////////////////////////////////////////////////////// + + /** EqualsExpr ::= `=' Expr + */ + def equalsExpr(): Tree = { + accept(EQUALS); + expr() + } + + /** Exprs ::= Expr {`,' Expr} [ `:' `_' `*' ] + */ + def exprs(): List[Tree] = { + val ts = new ListBuffer[Tree] + expr(true, false); + while (in.token == COMMA) { + in.nextToken(); ts += expr(true, false) + } + ts.toList + } + + /** Expr ::= Bindings `=>' Expr + * | Expr1 + * ResultExpr ::= Bindings `=>' Block + * | Expr1 + * Expr1 ::= if (' Expr `)' [NL] Expr [[`;'] else Expr] + * | try `{' block `}' [catch `{' caseClauses `}'] [finally Expr] + * | while `(' Expr `)' [NL] Expr + * | do Expr [SEP] while `(' Expr `)' + * | for (`(' Enumerators `)' | '{' Enumerators '}') [NL] (yield) Expr + * | throw Expr + * | return [Expr] + * | [SimpleExpr `.'] Id `=' Expr + * | SimpleExpr ArgumentExprs `=' Expr + * | `.' SimpleExpr + * | PostfixExpr [`:' Type1] + * | PostfixExpr match `{' caseClauses `}' + * Bindings ::= Id [`:' Type1] + * | `(' [Binding {`,' Binding}] `)' + * Binding ::= Id [`:' Type] + */ + def expr(): Tree = + expr(false, false); + + def expr(isArgument: boolean, isInBlock: boolean): Tree = in.token match { + case IF => + val pos = in.skipToken(); + accept(LPAREN); + val cond = expr(); + accept(RPAREN); + newLineOpt(); + val thenp = expr(); + val elsep = + if (in.token == ELSE) { in.nextToken(); expr() } + else EmptyTree; + atPos(pos) { If(cond, thenp, elsep) } + case TRY => + atPos(in.skipToken()) { + accept(LBRACE); + val body = block(); + accept(RBRACE); + val catches = + if (in.token == CATCH) { + in.nextToken(); + accept(LBRACE); + val cases = caseClauses(); + accept(RBRACE); + cases + } else List(); + val finalizer = + if (in.token == FINALLY) { in.nextToken(); expr() } + else EmptyTree; + Try(body, catches, finalizer) + } + case WHILE => + val lname: Name = unit.fresh.newName("label$"); + val pos = in.skipToken(); + accept(LPAREN); + val cond = expr(); + accept(RPAREN); + newLineOpt(); + val body = expr(); + atPos(pos) { makeWhile(lname, cond, body) } + case DO => + val lname: Name = unit.fresh.newName("label$"); + val pos = in.skipToken(); + val body = expr(); + if (in.token == SEMI || in.token == NEWLINE) in.nextToken(); + accept(WHILE); + accept(LPAREN); + val cond = expr(); + accept(RPAREN); + atPos(pos) { makeDoWhile(lname, body, cond) } + case FOR => + atPos(in.skipToken()) { + val startToken = in.token; + accept(if (startToken == LBRACE) LBRACE else LPAREN); + val enums = enumerators(); + accept(if (startToken == LBRACE) RBRACE else RPAREN); + newLineOpt(); + if (in.token == YIELD) { + in.nextToken(); makeForYield(enums, expr()) + } else makeFor(enums, expr()) + } + case RETURN => + atPos(in.skipToken()) { + Return(if (isExprIntro) expr() else Literal(())) + } + case THROW => + atPos(in.skipToken()) { + Throw(expr()) + } + case DOT => + atPos(in.skipToken()) { + if (in.token == IDENTIFIER) makeClosure(simpleExpr()) + else { syntaxError("identifier expected", true); errorTermTree } + } + case _ => + var t = postfixExpr(); + if (in.token == EQUALS) { + t match { + case Ident(_) | Select(_, _) | Apply(_, _) => + t = atPos(in.skipToken()) { makeAssign(t, expr()) } + case _ => + } + } else if (in.token == COLON) { + val pos = in.skipToken(); + if (isArgument && in.token == USCORE) { + val pos1 = in.skipToken(); + if (in.token == IDENTIFIER && in.name == nme.STAR) { + in.nextToken(); + t = atPos(pos) { + Typed(t, atPos(pos1) { Ident(nme.WILDCARD_STAR.toTypeName) }) + } + } else { + syntaxError(in.currentPos, "`*' expected", true); + } + } else { + t = atPos(pos) { Typed(t, type1()) } + } + } else if (in.token == MATCH) { + t = atPos(in.skipToken()) { + accept(LBRACE); + val cases = caseClauses(); + accept(RBRACE); + Match(t, cases): Tree + } + } + if (in.token == ARROW) { + t = atPos(in.skipToken()) { + Function(convertToParams(t), if (isInBlock) block() else expr()) + } + } + t + } + + /** PostfixExpr ::= [`.'] InfixExpr [Id] + * InfixExpr ::= PrefixExpr + * | InfixExpr Id InfixExpr + */ + def postfixExpr(): Tree = { + val base = opstack; + var top = prefixExpr(); + while (in.token == IDENTIFIER) { + top = reduceStack( + true, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)); + opstack = OpInfo(top, in.name, in.currentPos) :: opstack; + ident(); + if (isExprIntro) { + top = prefixExpr(); + } else { + val topinfo = opstack.head; + opstack = opstack.tail; + return Select( + reduceStack(true, base, topinfo.operand, 0, true), + topinfo.operator.encode).setPos(topinfo.pos); + } + } + reduceStack(true, base, top, 0, true) + } + + /** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr + */ + def prefixExpr(): Tree = + if (in.token == IDENTIFIER && in.name == MINUS) { + val name = ident(); + in.token match { + case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => literal(false, true) + case _ => atPos(in.currentPos) { Select(simpleExpr(), name) } + } + } else if (in.token == IDENTIFIER && (in.name == PLUS || in.name == TILDE || in.name == BANG)) { + val pos = in.currentPos; + val name = ident(); + atPos(pos) { Select(simpleExpr(), name) } + } else { + simpleExpr() + } + + /* SimpleExpr ::= new SimpleType {`(' [Exprs] `)'} {`with' SimpleType} [TemplateBody] + * | SimpleExpr1 + * SimpleExpr1 ::= literal + * | xLiteral + * | StableRef + * | `(' [Expr] `)' + * | BlockExpr + * | SimpleExpr `.' Id + * | SimpleExpr TypeArgs + * | SimpleExpr1 ArgumentExprs + */ + def simpleExpr(): Tree = { + var t: Tree = null; + var isNew = false; + in.token match { + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | + SYMBOLLIT | TRUE | FALSE | NULL => + t = literal(false, false); + case XMLSTART => + t = xmlp.xLiteral; + //Console.println("successfully parsed XML at "+t); // DEBUG + case IDENTIFIER | THIS | SUPER => + t = stableRef(true, false); + case LPAREN => + val pos = in.skipToken(); + if (in.token == RPAREN) { + in.nextToken(); + t = Literal(()).setPos(pos); + } else { + t = expr(); + if (in.token == COMMA) { + val commapos = in.skipToken(); + val ts = new ListBuffer[Tree] + t ++ exprs(); + accept(RPAREN); + if (in.token == ARROW) { + t = atPos(pos) { + Function(ts.toList map convertToParam, TypeTree()) + } + } else { + syntaxError(commapos, "`)' expected", false); + } + } else { + accept(RPAREN); + } + } + case LBRACE => + t = blockExpr() + case NEW => + t = atPos(in.skipToken()) { + val parents = new ListBuffer[Tree] + simpleType(); + val argss = new ListBuffer[List[Tree]]; + if (in.token == LPAREN) + do { argss += argumentExprs() } while (in.token == LPAREN) + else argss += List(); + while (in.token == WITH) { + in.nextToken(); + parents += simpleType() + } + val stats = if (in.token == LBRACE) templateBody() else List(); + makeNew(parents.toList, stats, argss.toList) + } + isNew = true + case _ => + syntaxError("illegal start of simple expression", true); + t = errorTermTree + } + while (true) { + in.token match { + case DOT => + t = atPos(in.skipToken()) { Select(t, ident()) } + case LBRACKET => + t match { + case Ident(_) | Select(_, _) => + t = atPos(in.currentPos) { TypeApply(t, typeArgs()) } + case _ => + return t; + } + case LPAREN | LBRACE if (!isNew) => + t = atPos(in.currentPos) { Apply(t, argumentExprs()) } + case _ => + return t + } + isNew = false + } + null;//dummy + } + + /** ArgumentExprs ::= `(' [Exprs] `)' + * | BlockExpr + */ + def argumentExprs(): List[Tree] = { + if (in.token == LBRACE) { + List(blockExpr()) + } else { + accept(LPAREN); + val ts = if (in.token == RPAREN) List() else exprs(); + accept(RPAREN); + ts + } + } + + /** BlockExpr ::= `{' CaseClauses | Block `}' + */ + def blockExpr(): Tree = { + val res = atPos(accept(LBRACE)) { + if (in.token == CASE) makeVisitor(caseClauses()) + else block() + } + accept(RBRACE); + res + } + + /** Block ::= BlockStatSeq + */ + def block(): Tree = makeBlock(blockStatSeq(new ListBuffer[Tree])); + + /** CaseClauses ::= CaseClause {CaseClause} + */ + def caseClauses(): List[CaseDef] = { + val ts = new ListBuffer[CaseDef]; + do { ts += caseClause(); + } while (in.token == CASE); + ts.toList + } + + /** caseClause : =>= case Pattern [if PostfixExpr] `=>' Block + */ + def caseClause(): CaseDef = + atPos(accept(CASE)) { + val pat = pattern(); + val guard = + if (in.token == IF) { in.nextToken(); postfixExpr() } + else EmptyTree; + makeCaseDef(pat, guard, atPos(accept(ARROW))(block())) + } + + /** Enumerators ::= Generator {SEP Enumerator} + * Enumerator ::= Generator + * | Expr + */ + def enumerators(): List[Tree] = { + val enums = new ListBuffer[Tree] + generator(); + while (in.token == SEMI || in.token == NEWLINE) { + in.nextToken(); + enums += (if (in.token == VAL) generator() else expr()) + } + enums.toList + } + + /** Generator ::= val Pattern1 `<-' Expr + */ + def generator(): Tree = + atPos(accept(VAL)) { + makeGenerator(pattern1(false), { accept(LARROW); expr() }) + } + +//////// PATTERNS //////////////////////////////////////////////////////////// + + /** Patterns ::= SeqPattern { , SeqPattern } */ + def patterns(): List[Tree] = { + val ts = new ListBuffer[Tree]; + ts += pattern(true); + while (in.token == COMMA) { + in.nextToken(); ts += pattern(true); + } + ts.toList + } + + /** Pattern ::= Pattern1 { `|' Pattern1 } + * SeqPattern ::= SeqPattern1 { `|' SeqPattern1 } + */ + def pattern(seqOK: boolean): Tree = { + val pos = in.currentPos; + val t = pattern1(seqOK); + if (in.token == IDENTIFIER && in.name == BAR) { + val ts = new ListBuffer[Tree] + t; + while (in.token == IDENTIFIER && in.name == BAR) { + in.nextToken(); ts += pattern1(seqOK); + } + atPos(pos) { makeAlternative(ts.toList) } + } else t + } + + def pattern(): Tree = pattern(false); + + /** Pattern1 ::= varid `:' Type1 + * | `_' `:' Type1 + * | Pattern2 + * SeqPattern1 ::= varid `:' Type1 + * | `_' `:' Type1 + * | [SeqPattern2] + */ + def pattern1(seqOK: boolean): Tree = + if (seqOK && !isExprIntro) { + atPos(in.currentPos) { Sequence(List()) } + } else { + val p = pattern2(seqOK); + p match { + case Ident(name) if (treeInfo.isVariableName(name) && in.token == COLON) => + atPos(in.skipToken()) { Typed(p, type1()) } + case _ => + p + } + } + + /* Pattern2 ::= varid [ @ Pattern3 ] + * | Pattern3 + * SeqPattern2 ::= varid [ @ SeqPattern3 ] + * | SeqPattern3 + */ + def pattern2(seqOK: boolean): Tree = { + val p = pattern3(seqOK); + if (in.token == AT) { + p match { + case Ident(name) => + if (name == nme.WILDCARD) { + in.nextToken(); pattern3(seqOK) + } else if (treeInfo.isVariableName(name)) { + atPos(in.skipToken()) { Bind(name, pattern3(seqOK)) } + } else { + p + } + case _ => + p + } + } else p + } + + /* Pattern3 ::= SimplePattern + * | SimplePattern {Id SimplePattern} + * SeqPattern3 ::= SeqSimplePattern [ '*' | '?' | '+' ] + * | SeqSimplePattern {Id SeqSimplePattern} + */ + def pattern3(seqOK: boolean): Tree = { + val base = opstack; + var top = simplePattern(seqOK); + if (seqOK && in.token == IDENTIFIER) { + if (in.name == STAR) + return atPos(in.skipToken())(Star(top)) + else if (in.name == PLUS) + return atPos(in.skipToken())(makePlus(top)) + else if (in.name == OPT) + return atPos(in.skipToken())(makeOpt(top)) + } + while (in.token == IDENTIFIER && in.name != BAR) { + top = reduceStack( + false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)); + opstack = OpInfo(top, in.name, in.currentPos) :: opstack; + ident(); + top = simplePattern(seqOK) + } + reduceStack(false, base, top, 0, true) + } + + /** SimplePattern ::= varid + * | `_' + * | literal + * | `<' xLiteralPattern + * | StableId [ `(' Patterns `)' ] + * | `(' [Pattern] `)' + * SimpleSeqPattern ::= varid + * | `_' + * | literal + * | `<' xLiteralPattern + * | StableId [ `(' Patterns `)' ] + * | `(' Patterns `)' + */ + def simplePattern(seqOK: boolean): Tree = in.token match { + case IDENTIFIER | THIS => + var t = stableId(); + in.token match { + case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => + t match { + case Ident(name) if name == nme.MINUS => + return literal(true, true); + case _ => + } + case _ => + } + if (in.token == LPAREN) { + atPos(in.skipToken()) { + val ps = if (in.token == RPAREN) List() else patterns(); + accept(RPAREN); + Apply(convertToTypeId(t), ps) + } + } else t + case USCORE => + atPos(in.skipToken()) { Ident(nme.WILDCARD) } + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | TRUE | FALSE | NULL => + literal(true, false) + case LPAREN => + val pos = in.skipToken(); + val p = + if (seqOK) atPos(pos) { makeSequence(patterns()) } + else if (in.token != RPAREN) pattern(false); + else Literal(()).setPos(pos); + accept(RPAREN); + p + case XMLSTART => + val r = xmlp.xLiteralPattern; + //Console.println("successfully parsed xml pattern "+r); DEBUG + r + case _ => + syntaxError("illegal start of simple pattern", true); + errorPatternTree + } + +////////// MODIFIERS //////////////////////////////////////////////////////////// + + /** Modifiers ::= {Modifier} + * Modifier ::= final + * | private [ "[" Id "]" ] + * | protected + * | override + * | abstract + */ + def modifiers(): Modifiers = { + def loop(mods: int): int = in.token match { + case ABSTRACT => + loop(addMod(mods, Flags.ABSTRACT)) + case FINAL => + loop(addMod(mods, Flags.FINAL)) + case SEALED => + loop(addMod(mods, Flags.SEALED)) + case PRIVATE => + loop(addMod(mods, Flags.PRIVATE)) + case PROTECTED => + loop(addMod(mods, Flags.PROTECTED)) + case OVERRIDE => + loop(addMod(mods, Flags.OVERRIDE)) + case IMPLICIT => + loop(addMod(mods, Flags.IMPLICIT)) + case _ => + mods + } + var mods = loop(0); + if ((mods & (Flags.ABSTRACT | Flags.OVERRIDE)) == (Flags.ABSTRACT | Flags.OVERRIDE)) + mods = mods & ~(Flags.ABSTRACT | Flags.OVERRIDE) | Flags.ABSOVERRIDE; + Modifiers(mods) + } + + /** LocalClassModifiers ::= {LocalClassModifier} + * LocalClassModifier ::= final + * | private + */ + def localClassModifiers(): Modifiers = { + def loop(mods: int): int = in.token match { + case ABSTRACT => + loop(addMod(mods, Flags.ABSTRACT)) + case FINAL => + loop(addMod(mods, Flags.FINAL)) + case SEALED => + loop(addMod(mods, Flags.SEALED)) + case _ => + mods + } + Modifiers(loop(0)) + } + + private def addMod(mods: int, mod: int): int = { + if ((mods & mod) != 0) + syntaxError(in.currentPos, "repeated modifier", false); + in.nextToken(); + mods | mod; + } + +//////// PARAMETERS ////////////////////////////////////////////////////////// + + /** ParamClauses ::= {`(' [Param {`,' Param}] ')'} + * [`(' implicit Param {`,' Param} `)'] + * Param ::= Id `:' ParamType + * ClassParamClauses ::= {`(' [ClassParam {`' ClassParam}] ')'} + * [`(' implicit ClassParam {`,' ClassParam} `)'] + * ClassParam ::= [[modifiers] (val | var)] Param + */ + def paramClauses(owner: Name, implicitViews: List[Tree], ofCaseClass: boolean): List[List[ValDef]] = { + var implicitmod = 0; + var caseParam = ofCaseClass; + def param(): ValDef = { + atPos(in.currentPos) { + var mods = Modifiers(Flags.PARAM); + if (owner.isTypeName) { + mods = modifiers() | Flags.PARAMACCESSOR; + if (in.token == VAL) { + in.nextToken() + } else if (in.token == VAR) { + mods = mods | Flags.MUTABLE; + in.nextToken() + } else { + if (mods.flags != Flags.PARAMACCESSOR) accept(VAL); + if (!(caseParam)) mods = mods | Flags.PRIVATE | Flags.LOCAL; + } + if (caseParam) mods = mods | Flags.CASEACCESSOR; + } + val name = ident(); + accept(COLON); + val bynamemod = if (in.token == ARROW) Flags.BYNAMEPARAM else 0; + ValDef(mods | implicitmod | bynamemod, name, paramType(), EmptyTree) + } + } + def paramClause(): List[ValDef] = { + val params = new ListBuffer[ValDef]; + if (in.token != RPAREN) { + if (in.token == IMPLICIT) { + if (!implicitViews.isEmpty) + syntaxError("cannot have both view bounds `<%' and implicit parameters", false); + in.nextToken(); + implicitmod = Flags.IMPLICIT + } + params += param(); + while (in.token == COMMA) { + in.nextToken(); params += param() + } + } + params.toList + } + val vds = new ListBuffer[List[ValDef]]; + val pos = in.currentPos; + while (implicitmod == 0 && in.token == LPAREN) { + in.nextToken(); + vds += paramClause(); + accept(RPAREN); + caseParam = false + } + val result = vds.toList; + if (owner == nme.CONSTRUCTOR && + (result.isEmpty || + (!result.head.isEmpty && result.head.head.mods.hasFlag(Flags.IMPLICIT)))) + if (in.token == LBRACKET) + syntaxError(pos, "no type parameters allowed here", false); + else + syntaxError(pos, "auxiliary constructor needs non-implicit parameter list", false); + addImplicitViews(owner, result, implicitViews) + } + + /** ParamType ::= Type | `=>' Type | Type `*' + */ + def paramType(): Tree = + if (in.token == ARROW) + atPos(in.skipToken()) { + AppliedTypeTree( + scalaDot(nme.BYNAME_PARAM_CLASS_NAME.toTypeName), List(typ())) + } + else { + val t = typ(); + if (in.token == IDENTIFIER && in.name == STAR) { + in.nextToken(); + atPos(t.pos) { + AppliedTypeTree( + scalaDot(nme.REPEATED_PARAM_CLASS_NAME.toTypeName), List(t)) + } + } else t + } + + /** TypeParamClauseOpt ::= [`[' TypeParam {`,' TypeParam} `]'] + * TypeParam ::= [`+' | `-'] FunTypeParam + * FunTypeParamClauseOpt ::= [`[' FunTypeParam {`,' FunTypeParam} `]'] + * FunTypeParam ::= Id TypeBounds + */ + def typeParamClauseOpt(owner: Name, implicitViews: ListBuffer[Tree]): List[AbsTypeDef] = { + def typeParam(): AbsTypeDef = { + var mods = Modifiers(Flags.PARAM); + if (owner.isTypeName && in.token == IDENTIFIER) { + if (in.name == PLUS) { + in.nextToken(); + mods = mods | Flags.COVARIANT; + } else if (in.name == MINUS) { + in.nextToken(); + mods = mods | Flags.CONTRAVARIANT; + } + } + val pname = ident(); + val param = atPos(in.currentPos) { typeBounds(mods, pname) } + if (in.token == VIEWBOUND && (implicitViews != null)) + implicitViews += atPos(in.skipToken()) { + makeFunctionTypeTree(List(Ident(pname.toTypeName)), typ()) + } + param + } + val params = new ListBuffer[AbsTypeDef]; + if (in.token == LBRACKET) { + in.nextToken(); + params += typeParam(); + while (in.token == COMMA) { + in.nextToken(); + params += typeParam(); + } + accept(RBRACKET); + } + params.toList + } + + /** TypeBounds ::= [`>:' Type] [`<:' Type] + */ + def typeBounds(mods: Modifiers, name: Name): AbsTypeDef = { + def bound(tok: int, default: Name): Tree = + if (in.token == tok) { in.nextToken(); typ() } + else scalaDot(default.toTypeName); + AbsTypeDef(mods, name.toTypeName, + bound(SUPERTYPE, nme.All), + bound(SUBTYPE, nme.Any)) + } + +//////// DEFS //////////////////////////////////////////////////////////////// + + + /** Import ::= import ImportExpr {`,' ImportExpr} + */ + def importClause(): List[Tree] = { + accept(IMPORT); + val ts = new ListBuffer[Tree] + importExpr(); + while (in.token == COMMA) { + in.nextToken(); ts += importExpr(); + } + ts.toList + } + + /** ImportRef ::= StableId `.' (Id | `_' | ImportSelectors) + */ + def importExpr(): Tree = + atPos(in.currentPos) { + var t: Tree = null; + var pos = 0; + if (in.token == THIS) { + t = atPos(in.currentPos) { This(nme.EMPTY.toTypeName) } + t = atPos(accept(DOT)) { Select(t, ident()) } + pos = accept(DOT); + } else { + val i = atPos(in.currentPos) { Ident(ident()) } + pos = accept(DOT); + if (in.token == THIS) { + in.nextToken(); + t = atPos(i.pos) { This(i.name.toTypeName) } + t = atPos(accept(DOT)) { Select(t, ident()) } + pos = accept(DOT); + } else { + t = i; + } + } + def loop: Tree = + if (in.token == USCORE) { + in.nextToken(); + Import(t, List(Pair(nme.WILDCARD, null))) + } else if (in.token == LBRACE) { + Import(t, importSelectors()) + } else { + val name = ident(); + if (in.token == DOT) { + t = atPos(pos) { Select(t, name) } + pos = accept(DOT); + loop + } else { + Import(t, List(Pair(name, name))); + } + } + loop + } + + /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}' + */ + def importSelectors(): List[Pair[Name, Name]] = { + val names = new ListBuffer[Pair[Name, Name]]; + accept(LBRACE); + var isLast = importSelector(names); + while (!isLast && in.token == COMMA) { + in.nextToken(); + isLast = importSelector(names); + } + accept(RBRACE); + names.toList + } + + /** ImportSelector ::= Id [`=>' Id | `=>' `_'] + */ + def importSelector(names: ListBuffer[Pair[Name, Name]]): boolean = + if (in.token == USCORE) { + in.nextToken(); names += Pair(nme.WILDCARD, null); true + } else { + val name = ident(); + names += Pair( + name, + if (in.token == ARROW) { + in.nextToken(); + if (in.token == USCORE) { in.nextToken(); nme.WILDCARD } else ident() + } else { + name + }); + false + } + + /** Def ::= val PatDef {`,' PatDef} + * | var VarDef {`,' VatDef} + * | def FunDef {`,' FunDef} + * | type TypeDef {`,' TypeDef} + * | TmplDef + * Dcl ::= val ValDcl {`,' ValDcl} + * | var ValDcl {`,' ValDcl} + * | def FunDcl {`,' FunDcl} + * | type TypeDcl {`,' TypeDcl} + */ + def defOrDcl(mods: Modifiers): List[Tree] = { + in.token match { + case VAL => + patDefOrDcl(mods); + case VAR => + varDefOrDcl(mods); + case DEF => + List(funDefOrDcl(mods)); + case TYPE => + in.nextToken(); + List(typeDefOrDcl(mods)) + case _ => + List(tmplDef(mods)) + } + } + + /** PatDef ::= Pattern2 {`,' Pattern2} [`:' Type] `=' Expr + * ValDcl ::= Id {`,' Id} `:' Type + */ + def patDefOrDcl(mods: Modifiers): List[Tree] = { + var newmods = mods; + var lhs = new ListBuffer[Tree]; + do { + in.nextToken(); + lhs += pattern2(false) + } while (in.token == COMMA); + val tp = typedOpt(); + val rhs = + if (tp.isEmpty || in.token == EQUALS) equalsExpr() + else { + newmods = newmods | Flags.DEFERRED; + EmptyTree + } + def mkDefs(p: Tree): List[Tree] = { + //Console.println("DEBUG: p = "+p.toString()); // DEBUG + val trees = + makePatDef(newmods, + if (tp.isEmpty) + p + else + Typed(p, tp), + rhs.duplicate) map atPos(p.pos); + if (rhs == EmptyTree) { + trees match { + case List(ValDef(_, _, _, EmptyTree)) => + case _ => syntaxError(p.pos, "pattern definition may not be abstract", false); + } + } + trees + } + for (val p <- lhs.toList; val d <- mkDefs(p)) yield d + } + + /** VarDef ::= Id {`,' Id} [`:' Type] `=' Expr + * | Id {`,' Id} `:' Type `=' `_' + * VarDcl ::= Id {`,' Id} `:' Type + */ + def varDefOrDcl(mods: Modifiers): List[Tree] = { + var newmods = mods | Flags.MUTABLE; + val lhs = new ListBuffer[Pair[Int, Name]]; + do { + lhs += Pair(in.skipToken(), ident()) + } while (in.token == COMMA); + val tp = typedOpt(); + val rhs = if (tp.isEmpty || in.token == EQUALS) { + accept(EQUALS); + if (tp != EmptyTree && in.token == USCORE) { + in.nextToken(); + EmptyTree + } else + expr(); + } else { + newmods = newmods | Flags.DEFERRED; + EmptyTree + } + for (val Pair(pos, name) <- lhs.toList) yield + atPos(pos) { ValDef(newmods, name, tp.duplicate, rhs.duplicate) } + } + + /** FunDef ::= FunSig `:' Type `=' Expr + * | this ParamClause ParamClauses `=' ConstrExpr + * FunDcl ::= FunSig `:' Type + * FunSig ::= id [FunTypeParamClause] ParamClauses + */ + def funDefOrDcl(mods: Modifiers): Tree = + atPos(in.skipToken()) { + if (in.token == THIS) { + in.nextToken(); + val vparamss = paramClauses(nme.CONSTRUCTOR, List(), false); + accept(EQUALS); + DefDef(mods, nme.CONSTRUCTOR, List(), vparamss, TypeTree(), constrExpr()) + } else { + var newmods = mods; + val name = ident(); + val implicitViews = new ListBuffer[Tree]; + val tparams = typeParamClauseOpt(name, implicitViews); + val vparamss = paramClauses(name, implicitViews.toList, false); + val restype = typedOpt(); + val rhs = + if (restype.isEmpty || in.token == EQUALS) equalsExpr(); + else { + newmods = newmods | Flags.DEFERRED; + EmptyTree + } + DefDef(newmods, name, tparams, vparamss, restype, rhs) + } + } + + /** ConstrExpr ::= SelfInvocation + * | `{' SelfInvocation {SEP BlockStat} `}' + * SelfInvocation ::= this ArgumentExpr + */ + def constrExpr(): Tree = + if (in.token == LBRACE) { + atPos(in.skipToken()) { + val statlist = new ListBuffer[Tree]; + statlist += selfInvocation(); + val stats = + if (in.token == SEMI || in.token == NEWLINE) { in.nextToken(); blockStatSeq(statlist) } + else statlist.toList; + accept(RBRACE); + makeBlock(stats) + } + } else selfInvocation(); + + /** SelfInvocation ::= this ArgumentExprs + */ + def selfInvocation(): Tree = + atPos(accept(THIS)) { Apply(Ident(nme.CONSTRUCTOR), argumentExprs()) } + + /** TypeDef ::= Id `=' Type + * TypeDcl ::= Id TypeBounds + */ + def typeDefOrDcl(mods: Modifiers): Tree = + atPos(in.currentPos) { + val name = ident().toTypeName; + in.token match { + case LBRACKET => + val tparams = typeParamClauseOpt(name, null); + accept(EQUALS); + AliasTypeDef(mods, name, tparams, typ()) + case EQUALS => + in.nextToken(); + AliasTypeDef(mods, name, List(), typ()) + case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | COMMA | RBRACE => + typeBounds(mods | Flags.DEFERRED, name) + case _ => + syntaxError("`=', `>:', or `<:' expected", true); + EmptyTree + } + } + + /** TmplDef ::= ([case] class | trait) ClassDef + * | [case] object ObjectDef + */ + def tmplDef(mods: Modifiers): Tree = in.token match { + case TRAIT => + classDef(mods | Flags.TRAIT | Flags.ABSTRACT); + case CLASS => + classDef(mods); + case CASECLASS => + classDef(mods | Flags.CASE); + case OBJECT => + objectDef(mods); + case CASEOBJECT => + objectDef(mods | Flags.CASE); + case _ => + syntaxError("illegal start of definition", true); + EmptyTree + } + + /** ClassDef ::= ClassSig RequiresTypeOpt ClassTemplate + * ClassSig ::= Id [TypeParamClause] {ClassParamClause} + */ + def classDef(mods: Modifiers): Tree = + atPos(in.skipToken()) { + val name = ident().toTypeName; + val implicitViews = new ListBuffer[Tree]; + val tparams = typeParamClauseOpt(name, implicitViews); + if (mods.hasFlag(Flags.CASE) && in.token != LPAREN) accept(LPAREN); + val vparamss = paramClauses(name, implicitViews.toList, mods.hasFlag(Flags.CASE)); + val thistpe = requiresTypeOpt(); + val template = classTemplate(mods, name, vparamss); + val mods1 = if (mods.hasFlag(Flags.TRAIT) && (template.body forall treeInfo.isInterfaceMember)) + mods | Flags.INTERFACE + else mods; + ClassDef(mods1, name, tparams, thistpe, template) + } + + /** ObjectDef ::= Id ClassTemplate + */ + def objectDef(mods: Modifiers): Tree = + atPos(in.skipToken()) { + val name = ident(); + val template = classTemplate(mods, name, List()); + ModuleDef(mods, name, template) + } + + /** ClassTemplate ::= [`extends' TemplateParents] [[NL] TemplateBody] + * TemplateParents ::= SimpleType {`(' [Exprs] `)'} {`with' SimpleType} + */ + def classTemplate(mods: Modifiers, name: Name, vparamss: List[List[ValDef]]): Template = { + val ret = atPos(in.currentPos) { + val parents = new ListBuffer[Tree]; + val argss = new ListBuffer[List[Tree]]; + if (in.token == EXTENDS) { + in.nextToken(); + val parent = simpleType(); + // System.err.println("classTempl: " + parent); + parents += parent; + if (in.token == LPAREN) + do { argss += argumentExprs() } while (in.token == LPAREN) + else argss += List(); + while (in.token == WITH) { + in.nextToken(); + parents += simpleType() + } + } else argss += List(); + if (name != nme.ScalaObject.toTypeName) + parents += scalaScalaObjectConstr; + if (mods.hasFlag(Flags.CASE)) parents += caseClassConstr; + val ps = parents.toList; + if (in.token == NEWLINE && in.next.token == LBRACE) in.nextToken(); + var body = + if (in.token == LBRACE) { + templateBody() + } else { + if (!(in.token == SEMI || in.token == NEWLINE || in.token == COMMA || in.token == RBRACE)) + syntaxError("`extends' or `{' expected", true); + List() + } + if (!mods.hasFlag(Flags.TRAIT)) Template(ps, vparamss, argss.toList, body) + else Template(ps, body) + } + ret; + } + +////////// TEMPLATES //////////////////////////////////////////////////////////// + + /** TemplateBody ::= `{' [TemplateStat {SEP TemplateStat}] `}' + */ + def templateBody(): List[Tree] = { + accept(LBRACE); + var body = templateStatSeq(); + if (body.isEmpty) body = List(EmptyTree); + accept(RBRACE); + body + } + + /** Refinement ::= `{' [RefineStat {SEP RefineStat}] `}' + */ + def refinement(): List[Tree] = { + accept(LBRACE); + val body = refineStatSeq(); + accept(RBRACE); + body + } + +/////// STATSEQS ////////////////////////////////////////////////////////////// + + /** Packaging ::= package QualId `{' TopStatSeq `}' + */ + def packaging(): Tree = { + atPos(accept(PACKAGE)) { + val pkg = qualId(); + accept(LBRACE); + val stats = topStatSeq(); + accept(RBRACE); + makePackaging(pkg, stats) + } + } + + /** TopStatSeq ::= [TopStat {SEP TopStat}] + * TopStat ::= AttributeClauses Modifiers ClsDef + * | Packaging + * | Import + * | + */ + def topStatSeq(): List[Tree] = { + val stats = new ListBuffer[Tree]; + while (in.token != RBRACE && in.token != EOF) { + if (in.token == PACKAGE) { + stats += packaging() + } else if (in.token == IMPORT) { + stats ++= importClause() + } else if (in.token == CLASS || + in.token == CASECLASS || + in.token == TRAIT || + in.token == OBJECT || + in.token == CASEOBJECT || + in.token == LBRACKET || + isModifier) { + val attrs = attributeClauses(); + (stats ++ + joinAttributes(attrs, joinComment(List(tmplDef(modifiers() | traitAttribute(attrs)))))) + } else if (in.token != SEMI && in.token != NEWLINE) { + syntaxError("illegal start of class or object definition", true); + } + if (in.token != RBRACE && in.token != EOF) acceptStatSep(); + } + stats.toList + } + + /** TemplateStatSeq ::= TemplateStat {SEP TemplateStat} + * TemplateStat ::= Import + * | AttributeClauses Modifiers Def + * | AttributeClauses Modifiers Dcl + * | Expr + * | + */ + def templateStatSeq(): List[Tree] = { + val stats = new ListBuffer[Tree]; + while (in.token != RBRACE && in.token != EOF) { + if (in.token == IMPORT) { + stats ++= importClause() + } else if (isExprIntro) { + stats += expr() + } else if (isDefIntro || isModifier || in.token == LBRACKET) { + val attrs = attributeClauses(); + (stats ++ + joinAttributes(attrs, joinComment(defOrDcl(modifiers() | traitAttribute(attrs))))) + } else if (in.token != SEMI && in.token != NEWLINE) { + syntaxError("illegal start of definition", true); + } + if (in.token != RBRACE) acceptStatSep(); + } + stats.toList + } + + /** AttributeClauses ::= {AttributeClause} + * AttributeClause ::= `[' Attribute {`,' Attribute} `]' [NL] + */ + def attributeClauses(): List[Tree] = { + var attrs = new ListBuffer[Tree]; + while (in.token == LBRACKET) { + in.nextToken(); + attrs += attribute(); + while (in.token == COMMA) { + in.nextToken(); + attrs += attribute() + } + accept(RBRACKET); + newLineOpt(); + } + attrs.toList + } + + /** Attribute ::= StableId [TypeArgs] [`(' [Exprs] `)'] + */ + def attribute(): Tree = { + val pos = in.currentPos; + var t: Tree = convertToTypeId(stableId()); + if (in.token == LBRACKET) + t = atPos(in.currentPos)(AppliedTypeTree(t, typeArgs())); + val args = if (in.token == LPAREN) argumentExprs() else List(); + atPos(pos) { New(t, List(args)) } + } + + def traitAttribute(attrs: List[Tree]) = { + def isTraitAttribute(attr: Tree) = attr match { + case Apply(Select(New(Ident(name)), constr), List()) if (name.toString() == "_trait_") => + true + case _ => + false + } + if (attrs exists isTraitAttribute) Flags.TRAIT else 0 + } + + def joinAttributes(attrs: List[Tree], defs: List[Tree]): List[Tree] = + defs map (defn => + (attrs :\ defn) ((attr, tree) => Attributed(attr, tree) setPos attr.pos)); + + /** RefineStatSeq ::= RefineStat {SEP RefineStat} + * RefineStat ::= Dcl + * | type TypeDef + * | + */ + def refineStatSeq(): List[Tree] = { + val stats = new ListBuffer[Tree]; + while (in.token != RBRACE && in.token != EOF) { + if (isDclIntro) { + stats ++= joinComment(defOrDcl(NoMods)) + } else if (in.token != SEMI && in.token != NEWLINE) { + syntaxError("illegal start of declaration", true); + } + if (in.token != RBRACE) acceptStatSep(); + } + stats.toList + } + + /** BlockStatSeq ::= { BlockStat SEP } [Expr] + * BlockStat ::= Import + * | Def + * | LocalModifiers TmplDef + * | Expr + * | + */ + def blockStatSeq(stats: ListBuffer[Tree]): List[Tree] = { + while ((in.token != RBRACE) && (in.token != EOF) && (in.token != CASE)) { + if (in.token == IMPORT) { + stats ++= importClause(); + acceptStatSep(); + } else if (isExprIntro) { + stats += expr(false, true); + if (in.token != RBRACE && in.token != CASE) acceptStatSep(); + } else if (isDefIntro) { + stats ++= defOrDcl(NoMods); + acceptStatSep(); + if (in.token == RBRACE || in.token == CASE) { + stats += Literal(()).setPos(in.currentPos) + } + } else if (isLocalModifier) { + stats += tmplDef(localClassModifiers()); + acceptStatSep(); + if (in.token == RBRACE || in.token == CASE) { + stats += Literal(()).setPos(in.currentPos) + } + } else if (in.token == SEMI || in.token == NEWLINE) { + in.nextToken(); + } else { + syntaxError("illegal start of statement", true); + } + } + stats.toList + } + + /** CompilationUnit ::= package QualId SEP TopStatSeq + * | package QualId `{' TopStatSeq `}' + * | TopStatSeq + */ + def compilationUnit(): Tree = + atPos(in.currentPos) { + if (in.token == PACKAGE) { + in.nextToken(); + val pkg = qualId(); + if (in.token == SEMI || in.token == NEWLINE) { + in.nextToken(); + makePackaging(pkg, topStatSeq()) + } else { + accept(LBRACE); + val t = makePackaging(pkg, topStatSeq()); + accept(RBRACE); + if (in.token == SEMI || in.token == NEWLINE) in.nextToken(); + t + } + } else { + makePackaging(Ident(nme.EMPTY_PACKAGE_NAME), topStatSeq()) + } + } + } +} + + + +// LocalWords: SOcos diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala new file mode 100644 index 0000000000..5fbf54834e --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -0,0 +1,908 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +import Tokens._; +import scala.tools.nsc.util.{Position, SourceFile} +import SourceFile.{LF, FF, CR, SU} +import scala.tools.nsc.util.CharArrayReader; + +[_trait_] abstract class Scanners: SyntaxAnalyzer { + + import global._; + + /** A class for representing a token's data. */ + class TokenData { + + /** the next token */ + var token: int = EMPTY; + + /** the token's position */ + protected var pos: int = 0; + + /** the first character position after the previous token */ + var lastPos: int = 0; + + def currentPos = pos - 1; + + + /** the name of an identifier or token */ + var name: Name = null; + + /** the base of a number */ + var base: int = 0; + + def copyFrom(td: TokenData) = { + this.token = td.token; + this.pos = td.pos; + this.lastPos = td.lastPos; + this.name = td.name; + this.base = td.base; + } + } + + /** A scanner for the programming language Scala. + * + * @author Matthias Zenger, Martin Odersky, Burak Emir + * @version 1.1 + */ + class Scanner(unit: CompilationUnit) extends TokenData { + + import Tokens._; + import java.lang.{Integer, Long, Float, Double, Character} + + /** Character input reader + */ + val in = new CharArrayReader(unit.source.getContent(), true, syntaxError); + + /** character buffer for literals + */ + val cbuf = new StringBuffer(); + + /** append Unicode character to "lit" buffer + */ + protected def putChar(c: char) = cbuf.append(c); + + /** Clear buffer and set name */ + private def setName = { + name = newTermName(cbuf.toString()); + cbuf.setLength(0) + } + + /** buffer for the documentation comment + */ + var docBuffer: StringBuffer = null; + + /** add the given character to the documentation buffer + */ + protected def putDocChar(c: char): unit = + if (docBuffer != null) docBuffer.append(c); + + /** we need one token lookahead + */ + val next = new TokenData(); + val prev = new TokenData(); + + /** the last error position + */ + var errpos = -1; + + /** a stack which indicates whether line-ends can be statement separators + */ + var sepRegions: List[int] = List(); + +// Get next token ------------------------------------------------------------ + + /** read next token and return last position + */ + def skipToken(): int = { + val p = pos; nextToken(); + // XXX: account for off by one error //??? + p - 1 + } + + def nextToken(): unit = { + if (token == LPAREN) { + sepRegions = RPAREN :: sepRegions + } else if (token == LBRACKET) { + sepRegions = RBRACKET :: sepRegions + } else if (token == LBRACE) { + sepRegions = RBRACE :: sepRegions + } else if (token == CASE) { + sepRegions = ARROW :: sepRegions + } else if (token == RBRACE) { + while (!sepRegions.isEmpty && sepRegions.head != RBRACE) + sepRegions = sepRegions.tail; + if (!sepRegions.isEmpty) + sepRegions = sepRegions.tail + } else if (token == RBRACKET || token == RPAREN || token == ARROW) { + if (!sepRegions.isEmpty && sepRegions.head == token) + sepRegions = sepRegions.tail + } + + val lastToken = token; + if (next.token == EMPTY) { + fetchToken(); + } else { + this.copyFrom(next); + next.token = EMPTY + } + + if (token == CASE) { + prev.copyFrom(this); + fetchToken(); + if (token == CLASS) { + token = CASECLASS; + lastPos = prev.lastPos; + } else if (token == OBJECT) { + token = CASEOBJECT; + lastPos = prev.lastPos; + } else { + next.copyFrom(this); + this.copyFrom(prev); + } + } else if (token == SEMI) { + prev.copyFrom(this); + fetchToken(); + if (token != ELSE) { + next.copyFrom(this); + this.copyFrom(prev); + } + } + + if (afterLineEnd() && inLastOfStat(lastToken) && inFirstOfStat(token) && + (sepRegions.isEmpty || sepRegions.head == RBRACE)) { + next.copyFrom(this); + pos = in.lineStartPos; + token = NEWLINE +/* + } else if (lastToken == RBRACE) { + System.out.println("failing to insert NL after RBRACE: " + sepRegions + " " + + lastPos + " " + in.lineStartPos + " " + pos); +*/ + } +// System.out.println("token: " + toString());//DEBUG + } + + private def afterLineEnd() = ( + lastPos < in.lineStartPos && + (in.lineStartPos <= pos || + lastPos < in.lastLineStartPos && in.lastLineStartPos <= pos) + ); + + /** read next token + */ + private def fetchToken(): unit = { + if (token == EOF) return; + lastPos = in.cpos - 1; // Position.encode(in.cline, in.ccol); + //var index = bp; + while (true) { + in.ch match { + case ' ' | '\t' | CR | LF | FF => + in.next; + case _ => + pos = in.cpos; // Position.encode(in.cline, in.ccol); + in.ch match { + case '\u21D2' => + in.next; token = ARROW; + return; + case 'A' | 'B' | 'C' | 'D' | 'E' | + 'F' | 'G' | 'H' | 'I' | 'J' | + 'K' | 'L' | 'M' | 'N' | 'O' | + 'P' | 'Q' | 'R' | 'S' | 'T' | + 'U' | 'V' | 'W' | 'X' | 'Y' | + 'Z' | '$' | '_' | + 'a' | 'b' | 'c' | 'd' | 'e' | + 'f' | 'g' | 'h' | 'i' | 'j' | + 'k' | 'l' | 'm' | 'n' | 'o' | + 'p' | 'q' | 'r' | 's' | 't' | + 'u' | 'v' | 'w' | 'x' | 'y' | // scala-mode: need to understand multi-line case patterns + 'z' => + putChar(in.ch); + in.next; + getIdentRest; // scala-mode: wrong indent for multi-line case blocks + return; + + case '<' => // is XMLSTART? + val last = in.last; + in.next; + last match { + case ' '|'\t'|'\n'|'{'|'('|'>' if xml.Parsing.isNameStart(in.ch) => + token = XMLSTART; + case _ => + // Console.println("found '<', but last is '"+in.last+"'"); // DEBUG + putChar('<'); + getOperatorRest; + } + return; + + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | /*'<' | */ + '>' | '?' | ':' | '=' | '&' | + '|' | '\\' => + putChar(in.ch); + in.next; + getOperatorRest; // XXX + return; + case '/' => + in.next; + if (!skipComment()) { + putChar('/'); + getOperatorRest; + return; + } + + case '0' => + putChar(in.ch); + in.next; + if (in.ch == 'x' || in.ch == 'X') { + in.next; + base = 16 + } else { + base = 8; + } + getNumber; + return; // scala-mode: return is a keyword + case '1' | '2' | '3' | '4' | + '5' | '6' | '7' | '8' | '9' => + base = 10; + getNumber; + return; + case '`' => //" scala-mode: need to understand literals + getStringLit('`'); + token = IDENTIFIER; + return; + case '\"' => //" scala-mode: need to understand literals + getStringLit('\"'); + return; + case '\'' => + in.next; + in.ch match { + case 'A' | 'B' | 'C' | 'D' | 'E' | + 'F' | 'G' | 'H' | 'I' | 'J' | + 'K' | 'L' | 'M' | 'N' | 'O' | + 'P' | 'Q' | 'R' | 'S' | 'T' | + 'U' | 'V' | 'W' | 'X' | 'Y' | + 'Z' | '$' | '_' | + 'a' | 'b' | 'c' | 'd' | 'e' | + 'f' | 'g' | 'h' | 'i' | 'j' | + 'k' | 'l' | 'm' | 'n' | 'o' | + 'p' | 'q' | 'r' | 's' | 't' | + 'u' | 'v' | 'w' | 'x' | 'y' | + 'z' => + putChar(in.ch); + in.next; + if (in.ch != '\'') { + getIdentRest; + token = SYMBOLLIT; + return; + } + case _ => + if (Character.isUnicodeIdentifierStart(in.ch)) { + putChar(in.ch); + in.next; + if (in.ch != '\'') { + getIdentRest; + token = SYMBOLLIT; + return; + } + } else { + getlitch() + } + } + if (in.ch == '\'') { + in.next; + token = CHARLIT; + setName + } else { + syntaxError("unclosed character literal"); + } + return; + case '.' => + in.next; + if ('0' <= in.ch && in.ch <= '9') { + putChar('.'); getFraction; + } else { + token = DOT + } + return; + case ';' => + in.next; token = SEMI; + return; + case ',' => + in.next; token = COMMA; + return; + case '(' => //scala-mode: need to understand character quotes + in.next; token = LPAREN; + return; + case '{' => + in.next; token = LBRACE; + return; + case ')' => + in.next; token = RPAREN; + return; + case '}' => + in.next; token = RBRACE; + return; + case '[' => + in.next; token = LBRACKET; + return; + case ']' => + in.next; token = RBRACKET; + return; + case SU => + if (!in.hasNext) token = EOF; + else { + syntaxError("illegal character"); + in.next + } + return; + case _ => + if (Character.isUnicodeIdentifierStart(in.ch)) { + putChar(in.ch); + in.next; + getIdentRest; + } else if (isSpecial(in.ch)) { + putChar(in.ch); + getOperatorRest; + } else { + syntaxError("illegal character"); + in.next; + } + return; + } + } + } + } + + private def skipComment(): boolean = + if (in.ch == '/') { + do { + in.next; + } while ((in.ch != CR) && (in.ch != LF) && (in.ch != SU)); + true + } else if (in.ch == '*') { + docBuffer = null; + var openComments = 1; + in.next; + if (in.ch == '*') docBuffer = new StringBuffer("/**"); + while (openComments > 0) { + do { + do { + if (in.ch == '/') { + in.next; putDocChar(in.ch); + if (in.ch == '*') { + in.next; putDocChar(in.ch); + openComments = openComments + 1; + } + } + in.next; putDocChar(in.ch); + } while (in.ch != '*' && in.ch != SU); + while (in.ch == '*') { + in.next; putDocChar(in.ch); + } + } while (in.ch != '/' && in.ch != SU); + if (in.ch == '/') in.next; + else syntaxError("unclosed comment"); + openComments = openComments - 1; + } + true + } else { + false + } + + def inFirstOfStat(token: int) = token match { + case EOF | ELSE | CASE | EXTENDS | WITH | YIELD | CATCH | FINALLY | MATCH | + REQUIRES | COMMA | SEMI | NEWLINE | DOT | USCORE | COLON | EQUALS | ARROW | + LARROW | SUBTYPE | VIEWBOUND | SUPERTYPE | HASH | AT | + RPAREN | RBRACKET | RBRACE => + false + case _ => + true + } + + def inLastOfStat(token: int) = token match { + case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT | STRINGLIT | SYMBOLLIT | + IDENTIFIER | THIS | NULL | TRUE | FALSE | RETURN | USCORE | + RPAREN | RBRACKET | RBRACE => + true + case _ => + false + } + +// Identifiers --------------------------------------------------------------- + + def isIdentStart(c: char): boolean = ( + ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'a') || + (c == '_') || (c == '$') || + Character.isUnicodeIdentifierStart(c) + ); + + def isIdentPart(c: char) = ( + isIdentStart(c) || + ('0' <= c && c <= '9') || + Character.isUnicodeIdentifierPart(c) + ); + + def isSpecial(c: char) = { + val chtp = Character.getType(c); + chtp == Character.MATH_SYMBOL || chtp == Character.OTHER_SYMBOL; + } + + private def getIdentRest: unit = + while (true) { + in.ch match { + case 'A' | 'B' | 'C' | 'D' | 'E' | + 'F' | 'G' | 'H' | 'I' | 'J' | + 'K' | 'L' | 'M' | 'N' | 'O' | + 'P' | 'Q' | 'R' | 'S' | 'T' | + 'U' | 'V' | 'W' | 'X' | 'Y' | + 'Z' | '$' | + 'a' | 'b' | 'c' | 'd' | 'e' | + 'f' | 'g' | 'h' | 'i' | 'j' | + 'k' | 'l' | 'm' | 'n' | 'o' | + 'p' | 'q' | 'r' | 's' | 't' | + 'u' | 'v' | 'w' | 'x' | 'y' | + 'z' | + '0' | '1' | '2' | '3' | '4' | + '5' | '6' | '7' | '8' | '9' => + putChar(in.ch); + in.next; + case '_' => + putChar(in.ch); + in.next; + getIdentOrOperatorRest; + return; + case SU => + setName; + token = name2token(name); + return; + case _ => + if(java.lang.Character.isUnicodeIdentifierPart(in.ch)) { + putChar(in.ch); + in.next + } else { + setName; + token = name2token(name); + return + } + } + } + + private def getOperatorRest: unit = + while (true) { + in.ch match { + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | '<' | + '>' | '?' | ':' | '=' | '&' | + '|' | '\\' => + putChar(in.ch); + in.next + case '/' => + in.next; + if (skipComment()) { + setName; + token = name2token(name); + return; + } else { + putChar('/'); + } + case _ => + if (isSpecial(in.ch)) { + putChar(in.ch); + in.next; + } else { + setName; + token = name2token(name); + return; + } + } + } + + private def getIdentOrOperatorRest: unit = + if (isIdentPart(in.ch)) + getIdentRest + else in.ch match { + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | '<' | + '>' | '?' | ':' | '=' | '&' | + '|' | '\\' | '/' => + getOperatorRest; + case _ => + if (isSpecial(in.ch)) getOperatorRest + else { + setName; + token = name2token(name) + } + } + + private def getStringLit(delimiter: char): unit = { + in.next; + while (in.ch != delimiter && (in.isUnicode || in.ch != CR && in.ch != LF && in.ch != SU)) { + getlitch(); + } + if (in.ch == delimiter) { + token = STRINGLIT; + setName; + in.next + } else { + syntaxError("unclosed string literal"); + } + } + +// Literals ----------------------------------------------------------------- + + /** read next character in character or string literal: + */ + protected def getlitch() = + if (in.ch == '\\') { + in.next; + if ('0' <= in.ch && in.ch <= '7') { + val leadch: char = in.ch; + var oct: int = in.digit2int(in.ch, 8); + in.next; + if ('0' <= in.ch && in.ch <= '7') { + oct = oct * 8 + in.digit2int(in.ch, 8); + in.next; + if (leadch <= '3' && '0' <= in.ch && in.ch <= '7') { + oct = oct * 8 + in.digit2int(in.ch, 8); + in.next; + } + } + putChar(oct.asInstanceOf[char]); + } else { + in.ch match { + case 'b' => putChar('\b') + case 't' => putChar('\t') + case 'n' => putChar('\n') + case 'f' => putChar('\f') + case 'r' => putChar('\r') + case '\"' => putChar('\"') + case '\'' => putChar('\'') + case '\\' => putChar('\\') + case _ => + syntaxError(in.cpos - 1, // Position.encode(in.cline, in.ccol - 1), + "invalid escape character"); + putChar(in.ch); + } + in.next + } + } else { + putChar(in.ch); + in.next; + } + + /** read fractional part and exponent of floating point number + * if one is present. + */ + protected def getFraction = { + token = DOUBLELIT; + while ('0' <= in.ch && in.ch <= '9') { + putChar(in.ch); + in.next; + } + if (in.ch == 'e' || in.ch == 'E') { + val lookahead = in.copy; + lookahead.next; + if (lookahead.ch == '+' || lookahead.ch == '-') { + lookahead.next; + } + if ('0' <= lookahead.ch && lookahead.ch <= '9') { + putChar(in.ch); + in.next; + if (in.ch == '+' || in.ch == '-') { + putChar(in.ch); + in.next; + } + while ('0' <= in.ch && in.ch <= '9') { + putChar(in.ch); + in.next; + } + } + token = DOUBLELIT; + } + if ((in.ch == 'd') || (in.ch == 'D')) { + putChar(in.ch); + in.next; + token = DOUBLELIT; + } else if ((in.ch == 'f') || (in.ch == 'F')) { + putChar(in.ch); + in.next; + token = FLOATLIT; + } + setName + } + + /** convert name to long value + */ + def intVal(negated: boolean): long = { + if (token == CHARLIT && !negated) { + if (name.length > 0) name(0) else 0 + } else { + var value: long = 0; + val divider = if (base == 10) 1 else 2; + val limit: long = + if (token == LONGLIT) Long.MAX_VALUE else Integer.MAX_VALUE; + var i = 0; + val len = name.length; + while (i < len) { + val d = in.digit2int(name(i), base); + if (d < 0) { + syntaxError("malformed integer number"); + return 0; + } + if (value < 0 || + limit / (base / divider) < value || + limit - (d / divider) < value * (base / divider) && + !(negated && limit == value * base - 1 + d)) { + syntaxError("integer number too large"); + return 0; + } + value = value * base + d; + i = i + 1; + } + if (negated) -value else value + } + } + + def intVal: long = intVal(false); + + /** convert name, base to double value + */ + def floatVal(negated: boolean): double = { + val limit: double = + if (token == DOUBLELIT) Double.MAX_VALUE else Float.MAX_VALUE; + try { + val value = Double.valueOf(name.toString()).doubleValue(); + if (value > limit) + syntaxError("floating point number too large"); + if (negated) -value else value + } catch { + case _: NumberFormatException => + syntaxError("malformed floating point number"); + 0.0 + } + } + + def floatVal: double = floatVal(false); + + /** read a number into name and set base + */ + protected def getNumber:unit = { + while (in.digit2int(in.ch, if (base < 10) 10 else base) >= 0) { + putChar(in.ch); + in.next; + } + token = INTLIT; + if (base <= 10 && in.ch == '.') { + val lookahead = in.copy; + lookahead.next; + lookahead.ch match { + case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | + '8' | '9' | 'd' | 'D' | 'e' | 'E' | 'f' | 'F' => + putChar(in.ch); + in.next; + return getFraction + case _ => + if (!isIdentStart(lookahead.ch)) { + putChar(in.ch); + in.next; + return getFraction + } + } + } + if (base <= 10 && + (in.ch == 'e' || in.ch == 'E' || + in.ch == 'f' || in.ch == 'F' || + in.ch == 'd' || in.ch == 'D')) { + return getFraction + } + setName; + if (in.ch == 'l' || in.ch == 'L') { + in.next; + token = LONGLIT; + } + } + +// XML lexing---------------------------------------------------------------- + def xSync = { + token = NEWLINE; // avoid getting NEWLINE from nextToken if last was RBRACE + //in.next; + nextToken(); + } + +// Errors ----------------------------------------------------------------- + + /** generate an error at the given position + */ + def syntaxError(pos: int, msg: String): unit = { + unit.error(pos, msg); + token = ERROR; + errpos = pos; + } + + /** generate an error at the current token position + */ + def syntaxError(msg: String): unit = syntaxError(pos, msg); + +// Keywords ----------------------------------------------------------------- + + /** Keyword array; maps from name indices to tokens */ + private var key: Array[byte] = _; + private var maxKey = 0; + private var tokenName = new Array[Name](128); + + { + var tokenCount = 0; + + // Enter keywords + + def enterKeyword(n: Name, tokenId: int): unit = { + while (tokenId >= tokenName.length) { + val newTokName = new Array[Name](tokenName.length * 2); + System.arraycopy(tokenName, 0, newTokName, 0, newTokName.length); + tokenName = newTokName; + } + tokenName(tokenId) = n; + if (n.start > maxKey) maxKey = n.start; + if (tokenId >= tokenCount) tokenCount = tokenId + 1; + } + + enterKeyword(nme.ABSTRACTkw, ABSTRACT); + enterKeyword(nme.CASEkw, CASE); + enterKeyword(nme.CATCHkw, CATCH); + enterKeyword(nme.CLASSkw, CLASS); + enterKeyword(nme.DEFkw, DEF); + enterKeyword(nme.DOkw, DO); + enterKeyword(nme.ELSEkw, ELSE); + enterKeyword(nme.EXTENDSkw, EXTENDS); + enterKeyword(nme.FALSEkw, FALSE); + enterKeyword(nme.FINALkw, FINAL); + enterKeyword(nme.FINALLYkw, FINALLY); + enterKeyword(nme.FORkw, FOR); + enterKeyword(nme.IFkw, IF); + enterKeyword(nme.IMPLICITkw, IMPLICIT); + enterKeyword(nme.IMPORTkw, IMPORT); + enterKeyword(nme.MATCHkw, MATCH); + enterKeyword(nme.REQUIRESkw, REQUIRES); + enterKeyword(nme.NEWkw, NEW); + enterKeyword(nme.NULLkw, NULL); + enterKeyword(nme.OBJECTkw, OBJECT); + enterKeyword(nme.OVERRIDEkw, OVERRIDE); + enterKeyword(nme.PACKAGEkw, PACKAGE); + enterKeyword(nme.PRIVATEkw, PRIVATE); + enterKeyword(nme.PROTECTEDkw, PROTECTED); + enterKeyword(nme.RETURNkw, RETURN); + enterKeyword(nme.SEALEDkw, SEALED); + enterKeyword(nme.SUPERkw, SUPER); + enterKeyword(nme.THISkw, THIS); + enterKeyword(nme.THROWkw, THROW); + enterKeyword(nme.TRAITkw, TRAIT); + enterKeyword(nme.TRUEkw, TRUE); + enterKeyword(nme.TRYkw, TRY); + enterKeyword(nme.TYPEkw, TYPE); + enterKeyword(nme.VALkw, VAL); + enterKeyword(nme.VARkw, VAR); + enterKeyword(nme.WHILEkw, WHILE); + enterKeyword(nme.WITHkw, WITH); + enterKeyword(nme.YIELDkw, YIELD); + enterKeyword(nme.DOTkw, DOT); + enterKeyword(nme.USCOREkw, USCORE); + enterKeyword(nme.COLONkw, COLON); + enterKeyword(nme.EQUALSkw, EQUALS); + enterKeyword(nme.ARROWkw, ARROW); + enterKeyword(nme.LARROWkw, LARROW); + enterKeyword(nme.SUBTYPEkw, SUBTYPE); + enterKeyword(nme.VIEWBOUNDkw, VIEWBOUND); + enterKeyword(nme.SUPERTYPEkw, SUPERTYPE); + enterKeyword(nme.HASHkw, HASH); + enterKeyword(nme.ATkw, AT); + + // Build keyword array + key = new Array[byte](maxKey+1); + for (val i <- Iterator.range(0, maxKey + 1)) + key(i) = IDENTIFIER; + for (val j <- Iterator.range(0, tokenCount)) + if (tokenName(j) != null) + key(tokenName(j).start) = j.asInstanceOf[byte]; + + } + +// Token representation ----------------------------------------------------- + + /** Convert name to token */ + def name2token(name: Name): int = + if (name.start <= maxKey) key(name.start) else IDENTIFIER; + + /** Returns the string representation of given token. */ + def token2string(token: int): String = token match { + case IDENTIFIER => + "identifier"/* + \""+name+"\""*/ + case CHARLIT => + "character literal" + case INTLIT => + "integer literal" + case LONGLIT => + "long literal" + case FLOATLIT => + "float literal" + case DOUBLELIT => + "double literal" + case STRINGLIT => + "string literal" + case SYMBOLLIT => + "symbol literal" + case LPAREN => + "'('" + case RPAREN => + "')'" + case LBRACE => + "'{'" + case RBRACE => + "'}'" + case LBRACKET => + "'['" + case RBRACKET => + "']'" + case EOF => + "eof" + case ERROR => + "something" + case SEMI => + "';'" + case NEWLINE => + "';'" + case COMMA => + "','" + case CASECLASS => + "case class" + case CASEOBJECT => + "case object" + case XMLSTART => + "$XMLSTART$<" + case _ => + try { + "'" + tokenName(token) + "'" + } catch { + case _: ArrayIndexOutOfBoundsException => + "'<" + token + ">'" + case _: NullPointerException => + "'<(" + token + ")>'" + } + } + + override def toString() = token match { + case IDENTIFIER => + "id(" + name + ")" + case CHARLIT => + "char(" + intVal + ")" + case INTLIT => + "int(" + intVal + ")" + case LONGLIT => + "long(" + intVal + ")" + case FLOATLIT => + "float(" + floatVal + ")" + case DOUBLELIT => + "double(" + floatVal + ")" + case STRINGLIT => + "string(" + name + ")" + case SEMI => + ";" + case NEWLINE => + ";" + case COMMA => + "," + case _ => + token2string(token) + } + + /** INIT: read lookahead character and token. + */ + in.next; + nextToken(); + } +} diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala new file mode 100644 index 0000000000..4fc234bead --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala @@ -0,0 +1,349 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author buraq + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +import scala.tools.nsc.util.Position; +import scala.Iterator; +import scala.collection.immutable.{ Map, ListMap }; +import scala.collection.mutable; +import scala.xml.{Text,TextBuffer}; + +import symtab.Flags.MUTABLE; + +/** this class builds instance of Tree that represent XML */ +abstract class SymbolicXMLBuilder(make: TreeBuilder, p: Parsers # Parser, preserveWS: Boolean ) { + + val global: Global; + import global._; + import posAssigner.atPos; + + //import scala.tools.scalac.ast.{TreeList => myTreeList} + + var isPattern:Boolean = _; + + import nme.{ + _Attribute , + _MetaData , + _NamespaceBinding , + _NodeBuffer , + + _Null , + + _PrefixedAttribute , + _UnprefixedAttribute , + _Elem , + _Seq , + _immutable , + _mutable , + _append , + _plus , + _collection , + _toList , + _xml , + _Comment , + _CharData , + _Node , + _ProcInstr , + _Text , + _EntityRef , + _md, + _scope, + _tmpscope}; + + + // convenience methods + private def LL[A](x:A*):List[List[A]] = List(List(x:_*)); + + private def _scala(name: Name) = Select(Ident(nme.scala_), name); + + private def _scala_Seq = _scala( _Seq ); + private def _scala_xml(name: Name) = Select( _scala(_xml), name ); + + private def _scala_xml_MetaData = _scala_xml( _MetaData ); + private def _scala_xml_NamespaceBinding = _scala_xml( _NamespaceBinding ); + private def _scala_xml_Null = _scala_xml( _Null ); + private def _scala_xml_PrefixedAttribute = _scala_xml(_PrefixedAttribute); + private def _scala_xml_UnprefixedAttribute= _scala_xml(_UnprefixedAttribute); + private def _scala_xml_Node = _scala_xml( _Node ); + private def _scala_xml_NodeBuffer = _scala_xml( _NodeBuffer ); + private def _scala_xml_EntityRef = _scala_xml( _EntityRef ); + private def _scala_xml_Comment = _scala_xml( _Comment ); + private def _scala_xml_CharData = _scala_xml( _CharData ); + private def _scala_xml_ProcInstr = _scala_xml( _ProcInstr ); + private def _scala_xml_Text = _scala_xml( _Text ); + private def _scala_xml_Elem = _scala_xml( _Elem ); + private def _scala_xml_Attribute = _scala_xml( _Attribute ); + + + private def bufferToArray(buf: mutable.Buffer[Tree]): Array[Tree] = { + val arr = new Array[Tree]( buf.length ); + var i = 0; + for (val x <- buf.elements) { arr(i) = x; i = i + 1; } + arr; + } + + // create scala xml tree + + /** + * @arg namespace: a Tree of type defs.STRING_TYPE + * @arg label: a Tree of type defs.STRING_TYPE + * @todo map: a map of attributes !!! + */ + + protected def mkXML(pos: int, isPattern: boolean, pre: Tree, label: Tree, attrs: /*Array[*/Tree/*]*/ , scope:Tree, children: mutable.Buffer[Tree]): Tree = { + if( isPattern ) { + //val ts = new mutable.ArrayBuffer[Tree](); + convertToTextPat( children ); + atPos (pos) { + Apply( _scala_xml_Elem, List( + pre, label, Ident( nme.WILDCARD ) /* attributes? */ , Ident( nme.WILDCARD )) /* scope? */ ::: children.toList ) + } + } else { + var ab = List(pre, label, attrs, scope); + if(( children.length ) > 0 ) + ab = ab ::: List(Typed(makeXMLseq(pos, children), Ident(nme.WILDCARD_STAR.toTypeName))); + atPos(pos) { New( _scala_xml_Elem, List(ab) )} + } + } + + final def entityRef( pos:int, n: String ) = { + atPos(pos) { New( _scala_xml_EntityRef, LL(Literal(Constant( n )))) }; + + }; + // create scala.xml.Text here <: scala.xml.Node + final def text( pos: Int, txt:String ):Tree = { + //makeText( isPattern, gen.mkStringLit( txt )); + val txt1 = Literal(Constant(txt)); + atPos(pos) { + if( isPattern ) + makeTextPat( txt1 ); + else + makeText1( txt1 ); + } + } + + // create scala.xml.Text here <: scala.xml.Node + def makeTextPat(txt:Tree ) = Apply(_scala_xml_Text, List(txt)); + def makeText1(txt:Tree ) = New( _scala_xml_Text, LL( txt )); + + // create + def comment( pos: int, text: String ):Tree = + atPos(pos) { Comment( Literal(Constant(text))) }; + + // create + def charData( pos: int, txt: String ):Tree = + atPos(pos) { CharData( Literal(Constant(txt))) }; + + // create scala.xml.Text here <: scala.xml.Node + def procInstr( pos: int, target: String, txt: String ) = + atPos(pos) { ProcInstr(Literal(Constant(target)), Literal(Constant(txt))) }; + + protected def CharData(txt: Tree) = New( _scala_xml_CharData, LL(txt)); + protected def Comment(txt: Tree) = New( _scala_xml_Comment, LL(txt)); + protected def ProcInstr(target: Tree, txt: Tree) = + New(_scala_xml_ProcInstr, LL( target, txt )); + + + /** @todo: attributes */ + def makeXMLpat(pos: int, n: String, args: mutable.Buffer[Tree]): Tree = + mkXML(pos, + true, + Ident( nme.WILDCARD ), + Literal(Constant(n)), + null, //Predef.Array[Tree](), + null, + args); + + protected def convertToTextPat(t: Tree): Tree = t match { + case _:Literal => makeTextPat(t); + case _ => t + } + + protected def convertToTextPat(buf: mutable.Buffer[Tree]): Unit = { + var i = 0; while( i < buf.length ) { + val t1 = buf( i ); + val t2 = convertToTextPat( t1 ); + if (!t1.eq(t2)) { + buf.remove(i); + buf.insert(i,t2); + } + i = i + 1; + } + } + + def freshName(prefix:String): Name; + + def isEmptyText(t:Tree) = t match { + case Literal(Constant("")) => true; + case _ => false; + } + def makeXMLseq( pos:int, args:mutable.Buffer[Tree] ) = { + var _buffer = New( _scala_xml_NodeBuffer, List(Nil)); + val it = args.elements; + while( it.hasNext ) { + val t = it.next; + if( !isEmptyText( t )) { + _buffer = Apply(Select(_buffer, _plus), List( t )); + } + } + atPos(pos) { Select(_buffer, _toList) }; + } + + def makeXMLseqPat( pos:int, args:List[Tree] ) = atPos(pos) {Apply( _scala_Seq, args )}; + + + /** returns Some(prefix) if pre:name, None otherwise */ + def getPrefix( name:String ):Option[String] = { + val i = name.indexOf(':'); + if( i != -1 ) Some( name.substring(0, i) ) else None + } + + /** splits */ + protected def qualifiedAttr( pos:Int, namespace:String, name:String ):Pair[String,String] = { + getPrefix( name ) match { + case Some( pref ) => + val newLabel = name.substring( pref.length()+1, name.length() ); + // if( newLabel.indexOf(':') != -1 ) syntaxError + Pair( "namespace$"+pref, newLabel ); + case None => + Pair( namespace, name ); + } + } + protected def qualified( pos:Int, name:String ):Pair[String,String] = + getPrefix( name ) match { + case Some( pref ) => + val newLabel = name.substring( pref.length()+1, name.length() ); + // if( newLabel.indexOf(':') != -1 ) syntaxError + Pair( "namespace$"+pref, newLabel ); + case None => + Pair( "namespace$default", name ); + } + + + /** makes an element */ + def element(pos: int, qname: String, attrMap: mutable.Map[String,Tree], args: mutable.Buffer[Tree]): Tree = { + //Console.println("SymbolicXMLBuilder::element("+pos+","+qname+","+attrMap+","+args+")"); + var setNS = new mutable.HashMap[String, Tree]; + + var tlist: List[Tree] = List(); + + /* pre can be null */ + def handleNamespaceBinding(pre:String , uri:Tree): Unit = { + val t = Assign(Ident(_tmpscope), New( _scala_xml_NamespaceBinding, + LL(Literal(Constant(pre)), uri, Ident( _tmpscope)))); + tlist = t :: tlist; + //Console.println("SymbolicXMLBuilder::handleNamespaceBinding:"); + //Console.println(t.toString()); + } + + /* DEBUG */ + val attrIt = attrMap.keys; + while( attrIt.hasNext ) { + val z = attrIt.next; + if( z.startsWith("xmlns") ) { // handle namespace + val i = z.indexOf(':'); + if( i == -1 ) + handleNamespaceBinding(null, attrMap( z )); + //setNS.update("default", attrMap( z ) ); + else { + val zz = z.substring( i+1, z.length() ); + //setNS.update( zz, attrMap( z ) ); + handleNamespaceBinding(zz, attrMap( z )); + } + attrMap -= z; + } + } + + val moreNamespaces = (0 < tlist.length); + + /* */ + val i = qname.indexOf(':'); + + var newlabel = qname; + val pre = getPrefix(qname) match { + case Some(p) => + newlabel = qname.substring(p.length()+1, qname.length()); + p; + case None => + null + } + var tlist2: List[Tree] = List(); + + // make attributes + + def handlePrefixedAttribute(pre:String, key:String, value:Tree): Unit = { + val t = atPos(pos) { + Assign(Ident(_md), New( _scala_xml_PrefixedAttribute, + LL( + Literal(Constant(pre)), + Literal(Constant(key)), + value, + Ident(_md) + )))}; + tlist2 = t :: tlist2; + // Console.println("SymbolicXMLBuilder::handlePrefixed :"); + // Console.println(t.toString()); + } + + def handleUnprefixedAttribute(key:String, value:Tree): Unit = { + val t = atPos(pos) { + Assign(Ident(_md), New(_scala_xml_UnprefixedAttribute, + LL(Literal(Constant(key)),value,Ident(_md)) + ))}; + tlist2 = t :: tlist2; + } + + var it = attrMap.elements; + while (it.hasNext) { + val ansk = it.next; + getPrefix(ansk._1) match { + case Some(pre) => + val key = ansk._1.substring(pre.length()+1, ansk._1.length()); + handlePrefixedAttribute(pre, key, ansk._2); + case None => + handleUnprefixedAttribute(ansk._1, ansk._2); + } + } + // attrs + + + val moreAttributes = (0 < tlist2.length); + + var ts: List[Tree] = tlist; + var ts2: List[Tree] = List(); + + if(moreAttributes) { + ts2 = atPos(pos) {ValDef(Modifiers(MUTABLE), + _md, + _scala_xml_MetaData, + _scala_xml_Null)} :: tlist2; + } + if(moreNamespaces) { + ts = atPos(pos) { + ValDef(Modifiers(MUTABLE), + _tmpscope, + _scala_xml_NamespaceBinding, + Ident(_scope))} :: ts; + + ts2 = ValDef(NoMods, _scope, _scala_xml_NamespaceBinding, Ident(_tmpscope)) :: ts2; + } + + val makeSymbolicAttrs = { + if (moreAttributes) Ident(_md) else _scala_xml_Null; + } + + var t = mkXML(pos, + false, + Literal(Constant(pre)) /* can be null */ , + Literal(Constant(newlabel)):Tree, + makeSymbolicAttrs, + Ident(_scope), + args); + + atPos(pos) { Block(ts, Block( ts2, t)) } + } +} + diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala new file mode 100644 index 0000000000..b4a3b507b3 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -0,0 +1,25 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +/** An nsc sub-component. + */ +abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParsers with Scanners { + val phaseName = "parser"; + def newPhase(prev: Phase): StdPhase = new ParserPhase(prev); + class ParserPhase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) { + def apply(unit: global.CompilationUnit): unit = { + global.informProgress("parsing " + unit); + unit.body = new Parser(unit).parse(); + } + } + //Moez addition. I wished not to add/modify here, but the fact that Parsers + // are NOT accessible (because of Parsers' self type) except in SyntaxAnalyzer + // had bitten me, and thus I had to add the following code here. + def interpreterParse(unit: global.CompilationUnit): List[global.Tree] = + new Parser(unit).templateStatSeq() +} + diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala new file mode 100644 index 0000000000..b99ee08811 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -0,0 +1,97 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +object Tokens { + + /** special tokens */ + final val EMPTY = -3; + final val UNDEF = -2; + final val ERROR = -1; + final val EOF = 0; + + /** literals */ + final val CHARLIT = 1; + final val INTLIT = 2; + final val LONGLIT = 3; + final val FLOATLIT = 4; + final val DOUBLELIT = 5; + final val STRINGLIT = 6; + final val SYMBOLLIT = 7; + + /** identifier */ + final val IDENTIFIER = 10; + + /** keywords */ + final val IF = 20; + final val FOR = 21; + final val ELSE = 22; + final val THIS = 23; + final val NULL = 24; + final val NEW = 25; + final val WITH = 26; + final val SUPER = 27; + final val CASE = 28; + final val CASECLASS = 29; + final val CASEOBJECT = 30; + final val VAL = 31; + final val ABSTRACT = 32; + final val FINAL = 33; + final val PRIVATE = 34; + final val PROTECTED = 35; + final val OVERRIDE = 36; + final val IMPLICIT = 37; + final val VAR = 38; + final val DEF = 39; + final val TYPE = 40; + final val EXTENDS = 41; + final val TRUE = 42; + final val FALSE = 43; + final val OBJECT = 44; + final val CLASS = 45; + + final val IMPORT = 46; + final val PACKAGE = 47; + final val YIELD = 48; + final val DO = 49; + final val TRAIT = 50; + final val SEALED = 51; + final val THROW = 52; + final val TRY = 53; + final val CATCH = 54; + final val FINALLY = 55; + final val WHILE = 56; + final val RETURN = 57; + final val MATCH = 58; + final val REQUIRES = 59; + + /** special symbols */ + final val COMMA = 61; + final val SEMI = 62; + final val DOT = 63; + final val USCORE = 64; + final val COLON = 65; + final val EQUALS = 66; + final val LARROW = 67; + final val ARROW = 68; + final val NEWLINE = 69; + final val SUBTYPE = 70; + final val SUPERTYPE = 71; + final val HASH = 72; + final val AT = 73; + final val VIEWBOUND = 74; + + /** parenthesis */ + final val LPAREN = 90; + final val RPAREN = 91; + final val LBRACKET = 92; + final val RBRACKET = 93; + final val LBRACE = 94; + final val RBRACE = 95; + + /** XML mode */ + final val XMLSTART = 96; +} diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala new file mode 100644 index 0000000000..70355350a7 --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -0,0 +1,289 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ +// $Id$ +package scala.tools.nsc.ast.parser; + +import symtab.Flags._; +import util.ListBuffer; + +abstract class TreeBuilder { + + val global: Global; + import global._; + import posAssigner.atPos; + + def freshName(prefix: String): Name; + + def freshName(): Name = freshName("x$"); + + private object patvarTransformer extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Ident(name) if (treeInfo.isVariableName(name) && name != nme.WILDCARD) => + atPos(tree.pos)(Bind(name, Ident(nme.WILDCARD))) + case Typed(id @ Ident(name), tpt) if (treeInfo.isVariableName(name) && name != nme.WILDCARD) => + Bind(name, atPos(tree.pos)(Typed(Ident(nme.WILDCARD), tpt))) setPos id.pos + case Apply(fn @ Apply(_, _), args) => + copy.Apply(tree, transform(fn), transformTrees(args)) + case Apply(fn, args) => + copy.Apply(tree, fn, transformTrees(args)) + case Typed(expr, tpt) => + copy.Typed(tree, transform(expr), tpt) + case Bind(name, body) => + copy.Bind(tree, name, transform(body)) + case Sequence(_) | Alternative(_) | Star(_) => + super.transform(tree) + case _ => + tree + } + } + + /** Traverse pattern and collect all variable names in buffer */ + private object getvarTraverser extends Traverser { + val buf = new ListBuffer[Name]; + def init: Traverser = { buf.clear; this } + override def traverse(tree: Tree): unit = tree match { + case Bind(name, tpe) => + if ((name != nme.WILDCARD) && (buf.elements forall (name !=))) buf += name; + traverse(tpe) + case _ => super.traverse(tree) + } + } + + /** Returns list of all pattern variables without duplicates */ + private def getVariables(tree: Tree): List[Name] = { + getvarTraverser.init.traverse(tree); + getvarTraverser.buf.toList + } + + private def mkTuple(trees: List[Tree]): Tree = trees match { + case List() => Literal(()) + case List(tree) => tree + case _ => Apply(Select(Ident(nme.scala_), newTermName("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 { + case Ident(name) => Some(Pair(name, TypeTree())) + case Bind(name, Ident(nme.WILDCARD)) => Some(Pair(name, TypeTree())) + case Typed(Ident(name), tpt) => Some(Pair(name, tpt)) + case Bind(name, Typed(Ident(nme.WILDCARD), tpt)) => Some(Pair(name, tpt)) + case _ => None + } + + /** Create tree representing (unencoded) binary operation expression or pattern. */ + def makeBinop(isExpr: boolean, left: Tree, op: Name, right: Tree): Tree = { + if (isExpr) { + if (treeInfo.isLeftAssoc(op)) { + Apply(Select(left, op.encode), List(right)) + } else { + val x = freshName(); + Block( + List(ValDef(Modifiers(SYNTHETIC), x, TypeTree(), left)), + Apply(Select(right, op.encode), List(Ident(x)))) + } + } else { + Apply(Ident(op.encode.toTypeName), List(left, right)) + } + } + + /** Create tree representing an object creation <new parents { stats }> */ + def makeNew(parents: List[Tree], stats: List[Tree], argss: List[List[Tree]]): Tree = + if (parents.tail.isEmpty && stats.isEmpty) + New(parents.head, argss) + else { + val x = nme.ANON_CLASS_NAME.toTypeName; + Block( + List(ClassDef( + Modifiers(FINAL | SYNTHETIC), x, List(), TypeTree(), + Template(parents, List(List()), argss, stats))), + New(Ident(x), List(List()))) + } + + /** Create a tree represeting an assignment <lhs = rhs> */ + def makeAssign(lhs: Tree, rhs: Tree): Tree = lhs match { + case Apply(fn, args) => Apply(Select(fn, nme.update), args ::: List(rhs)) + case _ => Assign(lhs, rhs) + } + + /** A type tree corresponding to (possibly unary) intersection type */ + def makeIntersectionTypeTree(tps: List[Tree]): Tree = { + if (tps.tail.isEmpty) tps.head else CompoundTypeTree(Template(tps, List())) + } + + /** Create tree representing a while loop */ + def makeWhile(lname: Name, cond: Tree, body: Tree): Tree = { + val continu = Apply(Ident(lname), List()); + val rhs = If(cond, Block(List(body), continu), Literal(())); + LabelDef(lname, Nil, rhs) + } + + /** Create tree representing a do-while loop */ + def makeDoWhile(lname: Name, body: Tree, cond: Tree): Tree = { + val continu = Apply(Ident(lname), List()); + val rhs = Block(List(body), If(cond, continu, Literal(()))); + LabelDef(lname, Nil, rhs) + } + + /** Create block of statements `stats' */ + def makeBlock(stats: List[Tree]): Tree = { + if (stats.isEmpty) Literal(()) + else if (!stats.last.isTerm) Block(stats, Literal(())); + else if (stats.length == 1) stats(0) + else Block(stats.init, stats.last) + } + + /** Create tree for for-comprehension generator <val pat0 <- rhs0> */ + def makeGenerator(pat: Tree, rhs: Tree): Tree = { + val pat1 = patvarTransformer.transform(pat); + val rhs1 = matchVarPattern(pat1) match { + case Some(_) => + rhs + case None => + Apply( + Select(rhs, nme.filter), + List(makeVisitor(List( + CaseDef(pat1.duplicate, EmptyTree, Literal(true)), + CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(false)))))) + } + CaseDef(pat1, EmptyTree, rhs1) + } + + /** Create tree for for-comprehension <for (enums) do body> or + * <for (enums) yield body> where mapName and flatMapName are chosen + * corresponding to whether this is a for-do or a for-yield. + */ + private def makeFor(mapName: Name, flatMapName: Name, enums: List[Tree], body: Tree): Tree = { + + def makeCont(pat: Tree, body: Tree): Tree = matchVarPattern(pat) match { + case Some(Pair(name, tpt)) => + Function(List(ValDef(Modifiers(PARAM), name, tpt, EmptyTree)), body) + case None => + makeVisitor(List(CaseDef(pat, EmptyTree, body))) + } + + def makeBind(meth: Name, qual: Tree, pat: Tree, body: Tree): Tree = + Apply(Select(qual, meth), List(makeCont(pat, body))); + + atPos(enums.head.pos) { + enums match { + case CaseDef(pat, g, rhs) :: Nil => + makeBind(mapName, rhs, pat, body) + case CaseDef(pat, g, rhs) :: (rest @ (CaseDef(_, _, _) :: _)) => + makeBind(flatMapName, rhs, pat, makeFor(mapName, flatMapName, rest, body)) + case CaseDef(pat, g, rhs) :: test :: rest => + makeFor(mapName, flatMapName, + CaseDef(pat, g, makeBind(nme.filter, rhs, pat.duplicate, test)) :: rest, + body) + } + } + } + + /** Create tree for for-do comprehension <for (enums) body> */ + def makeFor(enums: List[Tree], body: Tree): Tree = + makeFor(nme.foreach, nme.foreach, enums, body); + + /** Create tree for for-yield comprehension <for (enums) yield body> */ + def makeForYield(enums: List[Tree], body: Tree): Tree = + makeFor(nme.map, nme.flatMap, enums, body); + + /** Create tree for a pattern alternative */ + def makeAlternative(ts: List[Tree]): Tree = { + def alternatives(t: Tree): List[Tree] = t match { + case Alternative(ts) => ts + case _ => List(t) + } + Alternative(for (val t <- ts; val a <- alternatives(t)) yield a) + } + + /** Create tree for a pattern sequence */ + def makeSequence(ts: List[Tree]): Tree = { + def elements(t: Tree): List[Tree] = t match { + case Sequence(ts) => ts + case _ => List(t) + } + Sequence(for (val t <- ts; val e <- elements(t)) yield e) + } + + /** Create tree for the p+ regex pattern, becomes p p* */ + def makePlus(p: Tree): Tree = + makeSequence(List(p, Star(p.duplicate))); + + /** Create tree for the p? regex pattern, becomes (p| ) */ + def makeOpt(p: Tree): Tree = + makeAlternative(List(p, Sequence(List()))); + + /** Create visitor <x => x match cases> */ + def makeVisitor(cases: List[CaseDef]): Tree = { + val x = freshName(); + Function(List(ValDef(Modifiers(PARAM | SYNTHETIC), x, TypeTree(), EmptyTree)), Match(Ident(x), cases)) + } + + /** Create tree for case definition <case pat if guard => rhs> */ + def makeCaseDef(pat: Tree, guard: Tree, rhs: Tree): CaseDef = { + CaseDef(patvarTransformer.transform(pat), guard, rhs); + } + + /** Create tree for pattern definition <mods val pat0 = rhs> */ + def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree): List[Tree] = matchVarPattern(pat) match { + case Some(Pair(name, tpt)) => + List(ValDef(mods, name, tpt, rhs)) + + case None => + // in case there are no variables in pattern + // val p = e ==> e.match (case p => ()) + // + // in case there is exactly one variable in pattern + // val x_1 = e.match (case p => (x_1)) + // + // in case there are more variables in pattern + // val p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) + // val x_1 = t$._1 + // ... + // val x_N = t$._N + val pat1 = patvarTransformer.transform(pat); + val vars = getVariables(pat1); + val matchExpr = atPos(pat1.pos){ + Match(rhs, List(CaseDef(pat1, EmptyTree, mkTuple(vars map Ident)))) + } + vars match { + case List() => + List(matchExpr) + case List(vname) => + List(ValDef(mods, vname, TypeTree(), matchExpr)) + case _ => + val tmp = freshName(); + val firstDef = ValDef(Modifiers(PRIVATE | LOCAL | SYNTHETIC), tmp, TypeTree(), matchExpr); + var cnt = 0; + val restDefs = for (val v <- vars) yield { + cnt = cnt + 1; + ValDef(mods, v, TypeTree(), Select(Ident(tmp), newTermName("_" + cnt))) + } + firstDef :: restDefs + } + } + + /** Create a tree representing a function type */ + def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree( + Select(Ident(nme.scala_), newTypeName("Function" + argtpes.length)), + argtpes ::: List(restpe)); + + /** Append implicit view section if for `implicitViews' if nonempty */ + def addImplicitViews(owner: Name, vparamss: List[List[ValDef]], implicitViews: List[Tree]): List[List[ValDef]] = { + val mods = Modifiers(if (owner.isTypeName) PARAMACCESSOR | LOCAL | PRIVATE else PARAM); + def makeViewParam(tpt: Tree) = ValDef(mods | IMPLICIT, freshName("view$"), tpt, EmptyTree); + if (implicitViews.isEmpty) vparamss + else vparamss ::: List(implicitViews map makeViewParam) + } + + /** Create a tree representing a packaging */ + def makePackaging(pkg: Tree, stats: List[Tree]): PackageDef = pkg match { + case Ident(name) => + PackageDef(name, stats) + case Select(qual, name) => + makePackaging(qual, List(PackageDef(name, stats))) + } +} |