// adapmeth.beh -- Conversion of adaptive methods to traversals & visitors. // $Id: adapmeth.beh,v 1.2 2000/04/28 22:10:41 johan Exp $ Program { /** Convert an adaptive method to a plain method that calls a traversal with some visitors. */ private void convertAdaptiveMethods() bypassing VisitorSpec via MethodDef to-stop { MethodSignature, TraversalRef, TraversalSpec, ClassName_Commalist, VisitorSpec } { (@ ClassGraph cg; @) before ClassGraph (@ cg = host; @) (@ ClassDef def; @) before ClassDef (@ def = host; @) (@ String body; @) before MethodDef (@ body = null; @) (@ MethodSignature sig; @) before MethodSignature (@ sig = host; @) (@ TraversalRef trv; TraversalDef trvdef; @) (@ StrategyExpression strat; @) before TraversalRef (@ trv = host; strat = null; trvdef = def.findTraversalDef(trv); if (trvdef == null) { System.err.println("Error: " + sig.get_name() + ":" + " No such traversal method: " + trv); } else if (trvdef.numParms() != 1) { System.err.println("Error: " + sig.get_name() + ":" + " Traversal method " + trv + " has wrong number of parameters"); trvdef = null; } @) before TraversalSpec (@ // Generate a new traversal; add it to the class after we know the // visitor classes. String newname = "__trav_" + sig.get_name(); trv = TraversalRef.parse(newname); strat = host.get_strat(); trvdef = null; @) (@ ClassName_Commalist classes; @) before ClassName_Commalist (@ classes = host; @) before VisitorSpec (@ // Generate a new visitor class and add it to the class graph. // Johan: allow overriding String newname = host.make_newname("__V_" + def.get_classname() + "_" + sig.get_name()); classes = ClassName_Commalist.parse(newname); String parts = ""; if (sig.get_parms() != null) { Enumeration e = sig.get_parms().elements(); while (e.hasMoreElements()) { MethodParm parm = (MethodParm) e.nextElement(); parts += "<" + parm.get_name() + "> " + parm.get_type() + " "; } } JavaType ret = sig.get_returnType(); if (!ret.isVoid()) if (host.hasReturnValue()) host.setReturnType(ret); else parts += " " + ret + " "; host.ensureStartAndFinish(); String parents = ""; if (trvdef != null) { // Pre-existing traversal method; make the visitor extend the // traversal method's argument class. ClassName vispar = trvdef.getVisitorClassName(); parents += "*extends* " + vispar + " "; } // Johan: adaptive visitor can extend others // This is useful for some printing methods // This OVERRIDEs the trvdef definition, if it exists. parents = host.make_extends(parents); ClassDef visdef = ClassDef.parse("notparsed visitor " + newname + " = " + parts + parents + host + "."); cg.addClassDef(visdef); @) after AdaptiveMethodBody (@ if (trvdef == null && strat != null) { trvdef = TraversalDef.parse("traversal " + trv + "(" + classes + ")" + "{" + strat + "; }"); trvdef.fillInVisitorNames(); def.addMethod(trvdef); } body = "\n"; int i = 0; Enumeration ve = classes.elements(); boolean first = true; String args = "", starts = "", finishes = ""; while (ve.hasMoreElements()) { ClassName c = (ClassName) ve.nextElement(); body += " " + c + " v" + i + " = new " + c + "();\n"; if (first) first = false; else args += ", "; args += "v" + i; starts += " v" + i + ".start();\n"; finishes = " v" + i + ".finish();\n" + finishes; i++; } if (sig.get_parms() != null) { Enumeration pe = sig.get_parms().elements(); while (pe.hasMoreElements()) { MethodParm parm = (MethodParm) pe.nextElement(); ParmName name = parm.get_name(); body += " v0.set_" + name + "(" + name + ");\n"; } } body += starts; body += " " + trv.get_name() + "(" + args + ");\n"; body += finishes; if (!sig.get_returnType().isVoid()) { body += " return v0.get_return_val();\n"; } body += " "; @) after MethodDef (@ if (body != null) host.set_methodbody( VerbatimMethodBody.parse(Text.begin + body + Text.end)); @) } } VisitorSpec { /** Does the visitor have a return method? */ boolean hasReturnValue() via ClassMethods bypassing VisitorSpec to ReturnValue { before ReturnValue (@ return_val = true; @) } /** Set the return type of the visitor's return method, if it exists and doesn't already have a return type. */ boolean setReturnType(JavaType t) via ClassMethods bypassing VisitorSpec to ReturnValue { before ReturnValue (@ if (host.get_type() == null) host.set_type(t); @) } // JOHAN's fixme: This should (currently doesn't) check to see if we // are subclassing an existing visitor. Ifso, then we assume that it // will have start and finish methods, and thus we don't generate // any. The reason why we don't do this is that we don't have a // global overview; for all we know, we ARE extending a visitor, // it's just that it is in a later beh file. So instead of being // sometimes smart, we opt to be consistently stupid. /** Make sure the visitor has start and finish methods. */ void ensureStartAndFinish() via ClassMethods bypassing VisitorSpec to { Start, Finish } { (@ boolean has_start, has_finish; @) before Start (@ has_start = true; @) before Finish (@ has_finish = true; @) (@ Method_SList methods; @) before Method_SList (@ methods = host; @) after ClassMethods (@ if ((!has_start || !has_finish) && methods == null) host.set_methods(methods = new Method_SList()); if (!has_start) methods.addElement(new Start()); if (!has_finish) methods.addElement(new Finish()); @) } } VisitorSpec { String make_extends(String dflt) to VisitorExtendsSpec { start {{ return_val = dflt; }} before VisitorExtendsSpec {{ if (!dflt.equals("")) System.err.println("Warning: overriding Visitor name "+dflt+" to "+host.get_classname()); return_val = "*extends* "+host.get_classname()+" "; }} } String make_newname(String dflt) to VisitorNameSpec { start {{ return_val = dflt; }} before VisitorNameSpec {{ return_val = host.get_classname().toString(); }} } } ClassDef { TraversalDef findTraversalDef(TraversalRef ref) bypassing VisitorSpec to TraversalDef { before TraversalDef (@ if (host.get_name().equals(ref.get_name())) return_val = host; @) } } TraversalDef { int numParms() (@ return parms.size(); @) /** Return the classname of the visitor parameter. Assumes exactly one parameter. */ ClassName getVisitorClassName() via TraversalParms to ClassName { before ClassName (@ return_val = host; @) } }