/* ____ ____ ____ ____ ______ *\
** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
** /_____/\____/\___/\____/____/ **
** **
** $Id$
\* */
package scalac.ast;
import java.io.*;
import java.util.*;
import scalac.*;
import scalac.symtab.*;
import scalac.util.*;
import Tree.*;
/** A helper class for building trees
*
* @author Martin Odersky, Christine Roeckl
* @version 1.0
*/
public class TreeGen implements Kinds, Modifiers {
/********************************************************************************/
/********************************************************************************/
/** VARIABLES **/
/** the global environment
*/
protected Global global;
/** the global definitions
*/
protected Definitions definitions;
/** the tree factory
*/
public TreeFactory make;
/************************************************************************/
/************************************************************************/
/** CONSTRUCTORS **/
public TreeGen(Global global, TreeFactory make) {
this.global = global;
this.definitions = global.definitions;
this.make = make;
}
public TreeGen(Global global) {
this(global, global.make);
}
/*************************************************************************/
/*************************************************************************/
/** METHODS **/
/** Create a dummy symbol to be used for templates.
*/
public Symbol localDummy(int pos, Symbol owner) {
return new TermSymbol(pos, Name.EMPTY, owner, 0)
.setInfo(Type.NoType);
}
public Tree mkRef(int pos, Type pre, Symbol sym) {
if (pre.isSameAs(Type.localThisType) || pre.symbol().isRoot())
return Ident(pos, sym);
else
return Select(pos, mkStableId(pos, pre), sym);
}
public Tree mkRef(int pos, Symbol sym) {
return mkRef(pos, sym.owner().thisType(), sym);
}
/** Build and attribute stable identifier tree corresponding to given prefix.
*/
public Tree mkStableId(int pos, Type pre) {
switch (pre.expandModuleThis()) {
case ThisType(Symbol sym):
return make.This(pos, Ident(pos, sym)).setType(pre);
case SingleType(Type pre1, Symbol sym):
return mkRef(pos, pre1, sym);
default:
throw new ApplicationError();
}
}
/** Build and attribute tree corresponding to given type.
*/
public Tree mkType(int pos, Type type) {
return TypeTerm(pos, type);
}
/** Build and attribute tree array corresponding to given type array.
*/
public Tree[] mkTypes(int pos, Type[] types) {
Tree[] res = new Tree[types.length];
for (int i = 0; i < types.length; i++) {
res[i] = mkType(pos, types[i]);
}
return res;
}
/** Build and attribute tree corresponding to given type.
*/
public Tree TypeTerm(int pos, Type type) {
return make.TypeTerm(pos).setType(type);
}
/** Build and attribute tree corresponding to symbol's declaration.
*/
public Tree mkDef(int pos, Symbol sym) {
switch (sym.kind) {
case ERROR:
return make.Bad(pos).setSymbol(Symbol.ERROR).setType(Type.ErrorType);
case TYPE: case ALIAS:
return TypeDef(pos, sym);
case VAL:
if (sym.isMethod()) return DefDef(pos, sym, Tree.Empty);
else return Param(pos, sym);
default:
throw new ApplicationError();
}
}
/** Build and attribute tree array corresponding to given symbol's declarations.
*/
public Tree[] mkDefs(int pos, Symbol[] syms) {
Tree[] res = new Tree[syms.length];
for (int i = 0; i < syms.length; i++) {
res[i] = mkDef(pos, syms[i]);
}
return res;
}
/** Build a boolean constant tree.
*/
public Tree mkBoolean(int pos, boolean bool) {
return mkRef(pos, bool ? definitions.TRUE() : definitions.FALSE());
}
/** Build a tree to be used as a base class constructor for a template.
*/
public Tree mkParentConstr(int pos, Type parentType) {
switch (parentType) {
case TypeRef(Type pre, Symbol sym, Type[] args):
Tree ref = mkRef(pos, pre, sym.constructor());
Tree constr = (args.length == 0) ? ref
: TypeApply(ref, mkTypes(sym.pos, args));
switch (parentType) {
case MethodType(Symbol[] params, Type restpe):
assert params.length == 0 : parentType;
return Apply(constr, Tree.EMPTY_ARRAY);
default:
return constr;
}
default:
throw global.fail("invalid parent type", parentType);
}
}
/** Build an array of trees to be used as base classes for a template.
*/
public Tree[] mkParentConstrs(int pos, Type[] parents) {
Tree[] constrs = new Tree[parents.length];
for (int i = 0; i < parents.length; ++i)
constrs[i] = mkParentConstr(pos, parents[i]);
return constrs;
}
/** Build parameter sections corresponding to type.
*/
public ValDef[][] mkParams(int pos, Type type) {
switch (type) {
case PolyType(Symbol[] tparams, Type restype):
return mkParams(pos, restype);
case MethodType(Symbol[] vparams, Type restype):
ValDef[] params1 = mkParams(pos, vparams);
ValDef[][] paramss = mkParams(pos, restype);
if (paramss.length == 0) {
return new ValDef[][]{params1};
} else {
ValDef[][] paramss1 = new ValDef[paramss.length + 1][];
paramss1[0] = params1;
System.arraycopy(paramss, 0, paramss1, 1, paramss.length);
return paramss1;
}
default:
return new ValDef[][]{};
}
}
/** Build parameter section corresponding to given array of symbols .
*/
public ValDef[] mkParams(int pos, Symbol[] symbols) {
ValDef[] res = new ValDef[symbols.length];
for (int i = 0; i < symbols.length; i++) {
res[i] = Param(pos, symbols[i]);
}
return res;
}
/** Build type parameter section corresponding to given array of symbols .
*/
public TypeDef[] mkTypeParams(int pos, Symbol[] symbols) {
TypeDef[] res = new TypeDef[symbols.length];
for (int i = 0; i < symbols.length; i++) {
res[i] = (TypeDef)TypeDef(pos, symbols[i]);
}
return res;
}
/** Build type definition corresponding to given symbol .
*/
public TypeDef TypeDef(int pos, Symbol sym) {
Global.instance.nextPhase();
Type symtype = sym.info();
Global.instance.prevPhase();
return (TypeDef) make.TypeDef(
pos,
sym.flags & SOURCEFLAGS,
sym.name,
TypeTerm(pos, symtype))
.setSymbol(sym).setType(definitions.UNIT_TYPE);
}
public Tree TypeDef(Symbol sym) {
return TypeDef(sym.pos, sym);
}
/** Build parameter
*/
public ValDef Param(int pos, Symbol sym) {
global.log("use of obsolete Param method in TreeGen");
return (ValDef)ValDef(pos, sym, Tree.Empty);
}
public ValDef Param(Symbol sym) {
global.log("use of obsolete Param method in TreeGen");
return Param(sym.pos, sym);
}
public ValDef ValDef(int pos, Symbol sym) {
return (ValDef)ValDef(pos, sym, Tree.Empty);
}
public ValDef ValDef(Symbol sym) {
return Param(sym.pos, sym);
}
/** Build and attribute block with given statements, starting
* at given position. The type is the type of the last
* statement in the block.
*/
public Tree Block(int pos, Tree[] stats) {
Type tp = (stats.length == 0) ? definitions.UNIT_TYPE
: stats[stats.length - 1].type;
return make.Block(pos, stats).setType(tp);
}
/** Build and attribute non-empty block with given statements.
*/
public Tree Block(Tree[] stats) {
return Block(stats[0].pos, stats);
}
public Tree Typed(Tree tree, Type tp) {
return make.Typed(tree.pos, tree, TypeTerm(tree.pos, tp)).setType(tp);
}
/** Build and attribute the assignment lhs = rhs
*/
public Tree Assign(int pos, Tree lhs, Tree rhs) {
return make.Assign(pos, lhs, rhs).setType(definitions.UNIT_TYPE);
}
public Tree Assign(Tree lhs, Tree rhs) {
return Assign(lhs.pos, lhs, rhs);
}
/** Build and attribute new B, given constructor expression B.
*/
public Tree New(Tree constr) {
Template templ = make.Template(
constr.pos, new Tree[]{constr}, Tree.EMPTY_ARRAY);
templ.setType(constr.type);
templ.setSymbol(localDummy(constr.pos, Symbol.NONE));
return make.New(constr.pos, templ).setType(constr.type); }
/** Build an allocation new P.C[TARGS](ARGS)
* given a (singleton) type P, class C, type arguments TARGS and arguments ARGS
*/
public Tree New(int pos, Type pre, Symbol clazz,
Type[] targs, Tree[] args) {
Tree constr = mkRef(pos, pre, clazz.constructor());
if (targs.length != 0)
constr = TypeApply(constr, mkTypes(pos, targs));
Tree base = Apply(constr, args);
return New(base);
}
/** Build a monomorphic allocation new P.C(ARGS)
* given a prefix P, class C and arguments ARGS
*/
public Tree New(int pos, Type pre, Symbol clazz, Tree[] args) {
return New(pos, pre, clazz, Type.EMPTY_ARRAY, args);
}
/** Build and attribute application node with given function
* and argument trees.
*/
public Tree Apply(int pos, Tree fn, Tree[] args) {
switch (fn.type) {
case Type.MethodType(Symbol[] vparams, Type restpe):
return make.Apply(pos, fn, args).setType(restpe);
default:
throw new ApplicationError("method type required", fn.type);
}
}
public Tree Apply(Tree fn, Tree[] args) {
return Apply(fn.pos, fn, args);
}
/** Build and attribute type application node with given function
* and argument trees.
*/
public Tree TypeApply(int pos, Tree fn, Tree[] args) {
switch (fn.type) {
case Type.PolyType(Symbol[] tparams, Type restpe):
return make.TypeApply(pos, fn, args)
.setType(restpe.subst(tparams, Tree.typeOf(args)));
default:
throw new ApplicationError("poly type required", fn.type);
}
}
public Tree TypeApply(Tree fn, Tree[] args) {
return TypeApply(fn.pos, fn, args);
}
/** Build and applied type node with given function
* and argument trees.
*/
public Tree AppliedType(int pos, Tree fn, Tree[] args) {
return make.AppliedType(pos, fn, args)
.setType(Type.appliedType(fn.type, Tree.typeOf(args)));
}
public Tree AppliedType(Tree fn, Tree[] args) {
return AppliedType(fn.pos, fn, args);
}
/** Build and attribute select node of given symbol.
* It is assumed that the prefix is not empty.
*/
public Tree Select(int pos, Tree qual, Symbol sym) {
assert sym.kind != NONE;
Global.instance.nextPhase();
Type symtype = qual.type.memberType(sym);
Global.instance.prevPhase();
if (sym.kind == VAL && qual.type.isStable() && symtype.isObjectType())
symtype = Type.singleType(qual.type, sym);
return make.Select(pos, qual, sym.name)
.setSymbol(sym).setType(symtype);
}
public Tree Select(Tree qual, Symbol sym) {
return Select(qual.pos, qual, sym);
}
public Tree Select(Tree qual, Name name) {
Symbol sym = qual.type.lookup(name);
assert (sym.kind != NONE && sym != Symbol.ERROR) : name + " from " + qual.type;
return Select(qual, sym);
}
/** Build and attribute ident node with given symbol.
*/
public Tree Ident(int pos, Symbol sym) {
Global.instance.nextPhase();
Type symtype = sym.type();
Global.instance.prevPhase();
if (sym.kind == VAL && symtype.isObjectType())
symtype = Type.singleType(sym.owner().thisType(), sym);
return make.Ident(pos, sym.name)
.setSymbol(sym).setType(symtype);
}
public Tree Ident(Symbol sym) {
return Ident(sym.pos, sym);
}
/** Build and attribute this node with given symbol.
*/
public Tree This(int pos, Symbol sym) {
return make.This(pos, Ident(pos, sym)).setType(sym.thisType());
}
/** Build and attribute super node with given type.
*/
public Tree Super(int pos, Type type) {
return make.Super(pos, TypeTerm(pos, type)).setType(type);
}
/** Build and attribute value/variable/let definition node whose signature
* corresponds to given symbol and which has given rhs.
*/
public Tree ValDef(int pos, Symbol sym, Tree rhs) {
Global.instance.nextPhase();
Type symtype = sym.type();
Global.instance.prevPhase();
return make.ValDef(pos,
sym.flags & SOURCEFLAGS,
sym.name,
TypeTerm(pos, symtype),
rhs)
.setSymbol(sym).setType(definitions.UNIT_TYPE);
}
public Tree ValDef(Symbol sym, Tree rhs) {
return ValDef(sym.pos, sym, rhs);
}
/** Build and attribute value/variable/let definition node whose signature
* corresponds to given symbol and which has given body.
*/
public Tree DefDef(int pos, Symbol sym, Tree body) {
Global.instance.nextPhase();
Type symtype = sym.type();
Global.instance.prevPhase();
return make.DefDef(pos,
sym.flags & SOURCEFLAGS,
sym.name,
mkTypeParams(pos, symtype.typeParams()),
mkParams(pos, symtype),
TypeTerm(pos, symtype.resultType()),
body)
.setSymbol(sym).setType(definitions.UNIT_TYPE);
}
public Tree DefDef(Symbol sym, Tree rhs) {
return DefDef(sym.pos, sym, rhs);
}
/** Generate class definition from class symbol, parent constructors, and body.
*/
public Tree ClassDef(int pos, Symbol clazz, Tree[] constrs, Tree[] body) {
Global.instance.nextPhase();
Type clazzinfo = clazz.info();
Type constrtype = clazz.constructor().info();
Global.instance.prevPhase();
switch (clazzinfo) {
case CompoundType(Type[] parents, Scope members):
Template templ = make.Template(pos, constrs, body);
templ.setType(clazzinfo);
templ.setSymbol(localDummy(pos, clazz.owner()));
return make.ClassDef(
pos,
clazz.flags & SOURCEFLAGS,
clazz.name,
mkTypeParams(pos, constrtype.typeParams()),
mkParams(pos, constrtype),
Tree.Empty,
templ)
.setSymbol(clazz).setType(definitions.UNIT_TYPE);
default:
throw new ApplicationError();
}
}
public Tree ClassDef(Symbol clazz, Tree[] constrs, Tree[] body) {
return ClassDef(clazz.pos, clazz, constrs, body);
}
/** Generate class definition from class symbol and body.
* All parents must by parameterless, or take unit parameters.
*/
public Tree ClassDef(int pos, Symbol clazz, Tree[] body) {
Global.instance.nextPhase();
Type clazzinfo = clazz.info();
Global.instance.prevPhase();
return ClassDef(pos, clazz, mkParentConstrs(pos, clazzinfo.parents()), body);
}
public Tree ClassDef(Symbol clazz, Tree[] body) {
return ClassDef(clazz.pos, clazz, body);
}
/** Build the expansion of (() => expr)
* This is:
* { class $clazz() extends scala.Function0 { def apply() = expr } ; new $clazz() }
*/
public Tree mkUnitFunction(Tree expr, Type tp, Symbol owner) {
int pos = expr.pos;
Type f0t = definitions.functionType(Type.EMPTY_ARRAY, tp);
ClassSymbol clazz = new ClassSymbol(
pos, Names.ANON_CLASS_NAME.toTypeName(), owner, 0);
clazz.setInfo(Type.compoundType(new Type[]{f0t}, new Scope(), clazz));
clazz.constructor().setInfo(
Type.MethodType(Symbol.EMPTY_ARRAY, clazz.typeConstructor()));
Symbol applyMeth = new TermSymbol(pos, Names.apply, clazz, FINAL)
.setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, tp));
clazz.info().members().enter(applyMeth);
Tree applyDef = DefDef(applyMeth, changeOwner(expr, owner, applyMeth));
Tree classDef = ClassDef(clazz, new Tree[]{applyDef});
Tree alloc = New(pos, Type.localThisType, clazz, Tree.EMPTY_ARRAY);
return Block(new Tree[]{classDef, alloc});
}
/** Change owner of all defined symbols from `prevOwner' to `newOwner'
*/
public Tree changeOwner(Tree tree, final Symbol prevOwner, final Symbol newOwner) {
Transformer lifter = new Transformer(global, global.currentPhase) {
public Tree transform(Tree tree) {
if (TreeInfo.isDefinition(tree)) {
Symbol sym = tree.symbol();
if (sym != null && sym.owner() == prevOwner) {
sym.setOwner(newOwner);
if (sym.kind == Kinds.CLASS)
sym.constructor().setOwner(newOwner);
}
}
return super.transform(tree);
}
};
return lifter.transform(tree);
}
}