Author: dda
Date: 2008-02-09 11:29:03 -0800 (Sat, 09 Feb 2008)
New Revision: 7991
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/parser/ASTIdentifier.java
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/CommonGenerator.java
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/ParseTreePrinter.java
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9Generator.java
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9ParseTreePrinter.java
Log:
Change 20080209-dda-7 by [EMAIL PROTECTED] on 2008-02-09 13:57:38 EST
in /Users/dda/laszlo/src/svn/openlaszlo/branches/devildog
for http://svn.openlaszlo.org/openlaszlo/branches/devildog
Summary: SWF9 mixin support
New Features:
Bugs Fixed: LPP-5266
Technical Reviewer: ptw (pending)
QA Reviewer: promanik (pending)
Doc Reviewer: (pending)
Documentation:
Release Notes:
Details:
The mixin support follows the design given by Tucker in LPP-5266.
In SWF9Generator, when a mixin is seen, it is recorded in mixinDef hashmap.
For each use of a mixin combination, the needed interstitials are recorded
in
the mixinRef hashmap, and all we need to do to the referencing class is
change
its superclass to be the interstitial.
Each mixin declaration AST is duplicated - one is processed
like a class and one like an interface.
(CommonGenerator:translateClassDirectivesBlock
now knows three ways to process a class - the third being to create a
property list).
The 'interface' version of the mixin remains in the parse tree and is
emitted
as the interface. The 'class' version of the mixin is what is put into
mixinDef.
After we are done processing the AST, we visit the mixinRef map and for
each entry,
we append a new class to the parse tree that implements the interstitial.
In unparsing, we need to track the current class name, since constructors
within
interstitials need that as their name (and not their original name which
matches
the mixin). Arguably this could be handled when the tree is created, but
currently
we are sharing all the contents of the interstitial AST with possibly other
interstitials.
Also in unparsing, bodies of functions for interfaces are handled
differently, so
we must track when we are in an interface.
The changes only affect the implementation of mixins for SWF9. It should be
possible to push this implementation into generic javascript eventually.
That seemed too big for this moment.
Tests:
regression tests: smoke/lzpix/weather for DHTML, SWF8 and henry's hello for
SWF9
mixin tests: for this, verified they compiled and checked contents by eye.
constructors in mixins
public/private methods in mixins
vars in mixins
mixins can appear after usage
overridden methods possible
multiple mixins
Not yet tested - converting LFC9 classes back to use mixins.
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/parser/ASTIdentifier.java
===================================================================
---
openlaszlo/branches/devildog/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/parser/ASTIdentifier.java
2008-02-09 19:28:12 UTC (rev 7990)
+++
openlaszlo/branches/devildog/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/parser/ASTIdentifier.java
2008-02-09 19:29:03 UTC (rev 7991)
@@ -14,6 +14,7 @@
private Type type = null;
private int hash = 0;
private boolean ellipsis = false;
+ private boolean isconstructor = false;
public static class Type {
public String typeName = null;
@@ -81,6 +82,14 @@
this.ellipsis = ellipsis;
}
+ public boolean isConstructor() {
+ return isconstructor;
+ }
+
+ public void setIsConstructor(boolean value) {
+ this.isconstructor = value;
+ }
+
public String toString() {
String dots = ellipsis ? "..." : "";
String typesuffix = "";
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/CommonGenerator.java
===================================================================
---
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/CommonGenerator.java
2008-02-09 19:28:12 UTC (rev 7990)
+++
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/CommonGenerator.java
2008-02-09 19:29:03 UTC (rev 7991)
@@ -386,7 +386,7 @@
List props = new ArrayList();
List classProps = new ArrayList();
List stmts = new ArrayList();
- translateClassDirectivesBlock(dirs, classnameString, props, classProps,
stmts);
+ translateClassDirectivesBlock(dirs, classnameString, props, classProps,
stmts, TranslateHow.AS_PROPERTY_LIST);
SimpleNode instanceProperties;
if (props.isEmpty()) {
@@ -432,9 +432,29 @@
return visitStatement(replNode);
}
- public void translateClassDirectivesBlock(SimpleNode[] dirs, String
classnameString, List props, List classProps, List stmts) {
+ /*enum*/
+ public static class TranslateHow {
+ /** Runtime initialization of properties and methods */
+ public static final TranslateHow AS_PROPERTY_LIST = new TranslateHow();
+
+ /** Static definition of properties and methods */
+ public static final TranslateHow AS_CLASS = new TranslateHow();
+
+ /** Only keep public methods, and omit the method implementation */
+ public static final TranslateHow AS_INTERFACE = new TranslateHow();
+
+ private TranslateHow() {}
+ }
+
+ // translate the class directives according to the 'how' argument.
+ // If how is AS_PROPERTY_LIST, function name/values and variable
+ // name/initvalues are added to either classProps (for statics)
+ // or props (for non-statics). For AS_CLASS, these
+ // are added to stmts, for AS_INTERFACE, the public ones are added
+ // to stmts. Any other thing in the directive list is added
+ // to stmts, no matter what 'how' is.
+ public void translateClassDirectivesBlock(SimpleNode[] dirs, String
classnameString, List props, List classProps, List stmts, TranslateHow how) {
dirs = (SimpleNode[])(flatten(dirs).toArray(new SimpleNode[0]));
- boolean keepClassMethods = options.getBoolean(Compiler.KEEP_CLASS_METHODS);
// Scope #pragma directives to block
Compiler.OptionMap savedOptions = options;
try {
@@ -445,30 +465,52 @@
// any modifiers, like 'static', 'final' are kept in mod.
ASTModifiedDefinition mod = null;
+ boolean ispublic = false;
+ boolean isconstructor = false;
+
if (n instanceof ASTModifiedDefinition) {
assert (n.getChildren().length == 1);
mod = (ASTModifiedDefinition)n;
if (mod.isStatic()) {
p = classProps;
}
+ ispublic = "public".equals(mod.getAccess());
n = n.get(0);
mod.verifyClassLevel(n);
}
- if (n instanceof ASTFunctionDeclaration && !keepClassMethods) {
+ if (n instanceof ASTFunctionDeclaration) {
SimpleNode[] c = n.getChildren();
assert c.length == 3;
- String fname = ((ASTIdentifier)c[0]).getName();
- // Transform constructor into '$lzsc$initialize' method
- if (classnameString.equals(fname)) {
- fname = "$lzsc$initialize";
- c[0] = new ASTIdentifier(fname);
+ ASTIdentifier fid = (ASTIdentifier)c[0];
+ String fname = fid.getName();
+ isconstructor = classnameString.equals(fname);
+ fid.setIsConstructor(isconstructor);
+
+ if (how == TranslateHow.AS_PROPERTY_LIST) {
+ // Transform constructor into '$lzsc$initialize' method
+ if (isconstructor) {
+ fname = "$lzsc$initialize";
+ c[0] = new ASTIdentifier(fname);
+ }
+ p.add(new ASTLiteral(fname));
+ SimpleNode funexpr = new ASTFunctionExpression(0);
+ funexpr.setBeginLocation(n.filename, n.beginLine, n.beginColumn);
+ funexpr.setChildren(c);
+ p.add(funexpr);
+ } else if (how == TranslateHow.AS_INTERFACE) {
+ if (!isconstructor && ispublic) {
+ stmts.add(n);
+ }
}
- p.add(new ASTLiteral(fname));
- SimpleNode funexpr = new ASTFunctionExpression(0);
- funexpr.setBeginLocation(n.filename, n.beginLine, n.beginColumn);
- funexpr.setChildren(c);
- p.add(funexpr);
- } else if (n instanceof ASTVariableStatement && !keepClassMethods) {
+ else {
+ assert how == TranslateHow.AS_CLASS;
+ if (mod != null) {
+ stmts.add(mod);
+ } else {
+ stmts.add(n);
+ }
+ }
+ } else if (n instanceof ASTVariableStatement && how ==
TranslateHow.AS_PROPERTY_LIST) {
SimpleNode [] c = n.getChildren();
for (int j = 0, len = c.length; j < len; j++) {
SimpleNode v = c[j];
@@ -481,17 +523,17 @@
}
}
} else if (n instanceof ASTClassDirectiveBlock) {
- translateClassDirectivesBlock(n.getChildren(), classnameString,
props, classProps, stmts);
+ translateClassDirectivesBlock(n.getChildren(), classnameString,
props, classProps, stmts, how);
} else if (n instanceof ASTClassIfDirective) {
Boolean value = evaluateCompileTimeConditional(n.get(0));
if (value == null) {
stmts.add(n);
} else if (value.booleanValue()) {
SimpleNode clause = n.get(1);
- translateClassDirectivesBlock(clause.getChildren(),
classnameString, props, classProps, stmts);
+ translateClassDirectivesBlock(clause.getChildren(),
classnameString, props, classProps, stmts, how);
} else if (n.size() > 2) {
SimpleNode clause = n.get(2);
- translateClassDirectivesBlock(clause.getChildren(),
classnameString, props, classProps, stmts);
+ translateClassDirectivesBlock(clause.getChildren(),
classnameString, props, classProps, stmts, how);
}
} else if (n instanceof ASTPragmaDirective) {
visitPragmaDirective(n, n.getChildren());
@@ -499,10 +541,10 @@
visitPassthroughDirective(n, n.getChildren());
stmts.add(n);
} else {
- if (keepClassMethods && mod != null) {
+ if (how == TranslateHow.AS_CLASS && mod != null) {
stmts.add(mod);
- }
- else {
+ } else if (how != TranslateHow.AS_INTERFACE) {
+ // interfaces can only have functions, those are handled above
stmts.add(n);
}
}
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
===================================================================
---
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
2008-02-09 19:28:12 UTC (rev 7990)
+++
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
2008-02-09 19:29:03 UTC (rev 7991)
@@ -419,7 +419,6 @@
public static String GENERATE_PREDICTABLE_TEMPS = "generatePredictableTemps";
public static String INCLUDES = "processIncludes";
public static String INSTR_STATS = "instrStats";
- public static String KEEP_CLASS_METHODS = "keepClassMethods";
public static String LINK = "link";
public static String RUNTIME = "runtime";
public static String METHOD_NAME = "methodName";
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/ParseTreePrinter.java
===================================================================
---
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/ParseTreePrinter.java
2008-02-09 19:28:12 UTC (rev 7990)
+++
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/ParseTreePrinter.java
2008-02-09 19:29:03 UTC (rev 7991)
@@ -60,6 +60,8 @@
String OPTIONAL_SEMI;
String OPENCURLY;
String CLOSECURLY;
+
+ private String currentClassName = null;
public ParseTreePrinter() {
this(false, false);
@@ -143,12 +145,24 @@
}
return(sb.toString());
}
-
- private String visit(SimpleNode node) {
+
+ /**
+ * Perform any actions that need to happen before
+ * children are visited. May be overridden.
+ */
+ public SimpleNode previsit(SimpleNode node) {
if (node instanceof PassThroughNode) {
node = ((PassThroughNode)node).realNode;
}
-
+ else if (node instanceof ASTClassDefinition) {
+ // classname is needed when emitting constructors for interstitials.
+ currentClassName = ((ASTIdentifier)(node.getChildren()[1])).getName();
+ }
+ return node;
+ }
+
+ private String visit(SimpleNode node) {
+ node = previsit(node);
int size = node.size();
SimpleNode[] childnodes = node.getChildren();
String[] children = new String[size];
@@ -697,15 +711,15 @@
}
public String visitFunctionDeclaration(SimpleNode node, String[] children) {
- return doFunctionDeclaration(node, children, true);
+ return doFunctionDeclaration(node, children, true, false);
}
public String visitFunctionExpression(SimpleNode node, String[] children) {
// Elide optional name if compressing, otherwise leave it for debugging
- return doFunctionDeclaration(node, children, this.compress ? false : true);
+ return doFunctionDeclaration(node, children, this.compress ? false : true,
false);
}
- String doFunctionDeclaration(SimpleNode node, String[] children, boolean
useName) {
+ String doFunctionDeclaration(SimpleNode node, String[] children, boolean
useName, boolean inmixin) {
String name, args, body;
if (children.length == 2) {
name = "";
@@ -718,13 +732,20 @@
} else {
return defaultVisitor(node, children);
}
- String loc = "";
+ String txt = "";
// Add location information if not compressing
if ((!this.compress) && (node.filename != null) && (node.beginLine != 0)) {
- loc = ("\n/* -*- file: " + Compiler.getLocationString(node) + " -*-
*/\n" );
+ txt = ("\n/* -*- file: " + Compiler.getLocationString(node) + " -*-
*/\n" );
}
- return
- loc + "function" + (useName ? (" " + name) : "") + OPENPAREN + args +
CLOSEPAREN + makeBlock(body);
+ txt += "function" + (useName ? (" " + name) : "") + OPENPAREN + args +
CLOSEPAREN;
+ if (!inmixin) {
+ txt += makeBlock(body);
+ }
+ else {
+ // This is an interface - no body needed
+ txt += SEMI;
+ }
+ return txt;
}
public String visitClassDefinition(SimpleNode node, String[] children) {
@@ -733,7 +754,12 @@
}
public String visitIdentifier(SimpleNode node, String[] children) {
- return ((ASTIdentifier)node).getName();
+ ASTIdentifier id = (ASTIdentifier)node;
+ if (id.isConstructor()) {
+ return currentClassName;
+ } else {
+ return id.getName();
+ }
}
static Double zero = new Double(0);
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9Generator.java
===================================================================
---
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9Generator.java
2008-02-09 19:28:12 UTC (rev 7990)
+++
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9Generator.java
2008-02-09 19:29:03 UTC (rev 7991)
@@ -88,6 +88,35 @@
*/
boolean inMethod = false;
+ /** values for the mixinRef map */
+ class MixinReference {
+ String mixinname;
+ String supername;
+ MixinReference(String m, String s) {
+ mixinname = m;
+ supername = s;
+ }
+ }
+
+ /** maps name of mixin to SimpleNode */
+ private Map mixinDef = new HashMap();
+
+ /** This map lists the interstitials (extra generated classes)
+ * needed to implement the mixins as used by the program.
+ * The key of the map is the name of interstitial class,
+ * and the data for each key is a MixinReference.
+ * Example 1: 'class C1 extends S1 with M1'
+ * M1$S1 => { M1, S1 }
+ * Example 2: 'class C2 extends S2 with M2, M3, M4'
+ * M2$M3$M4$S2 => { M2, M3$M4$S2 }
+ * M3$M4$S2 => { M3, M4$S2 }
+ * M4$S2 => { M4, S2 }
+ * Example 3: 'class C3 with M5, M6'
+ * M6$ => { M6, null }
+ * M5$M6$ => { M5, M6$ }
+ */
+ private Map mixinRef = new HashMap();
+
// override superclass version - we don't want to remap names
// of class variables
@@ -105,52 +134,117 @@
options.putBoolean(Compiler.DEBUG_SIMPLE, true);
}
+ /**
+ * The script compiler reserves parts of the name space for
+ * its own use. All of our generated names have dollar signs,
+ * so to be conservative, we do not allow class/mixin names
+ * with dollar signs.
+ * @throw CompilerError if name has a potential conflict
+ */
+ public void checkClassName(String name) {
+ if (name.indexOf('$') >= 0)
+ throw new CompilerError(name + ": class or mixin name may conflict with
internally generated names");
+ }
+
// override superclass method.
// For SWF9, we need to preserve
// the class definition, rather than converting it to
// a function call to dynamically create the class.
public SimpleNode visitClassDefinition(SimpleNode node, SimpleNode[]
children) {
- ASTIdentifier classortrait = (ASTIdentifier)children[0];
+ boolean isClass = "class".equals(((ASTIdentifier)children[0]).getName());
ASTIdentifier classname = (ASTIdentifier)children[1];
String classnameString = classname.getName();
SimpleNode superclass = children[2];
SimpleNode mixins = children[3];
- SimpleNode[] dirs = (SimpleNode [])(Arrays.asList(children).subList(4,
children.length).toArray(new SimpleNode[0]));
- List props = new ArrayList();
- List classProps = new ArrayList();
- List stmts = new ArrayList();
+ checkClassName(classnameString);
+ String supername = (superclass instanceof ASTEmptyExpression) ? null :
+ ((ASTIdentifier)superclass).getName();
+ SimpleNode mixinInterface = null;
- options.putBoolean(Compiler.KEEP_CLASS_METHODS, true);
+ if (!isClass) {
+ if (!(mixins instanceof ASTEmptyExpression)) {
+ // In theory it's possible to support mixins with mixins,
+ // but SWF8/DHTML don't, and is it useful?
+ throw new CompilerError("mixins not allowed with mixins");
+ }
+ if (supername != null) {
+ // It's probably easy to allow mixins to extend classes
+ // or other mixins, but it's not supported yet.
+ throw new CompilerError("mixins cannot extend anything");
+ }
+ }
+
+ if (!(mixins instanceof ASTEmptyExpression)) {
+ // create reference to mixin classes
+ int nmixins = mixins.size();
+ String superiname = supername; // iname == 'interstitial name'
+ for (int i=nmixins-1; i>=0; i--) {
+ String mixinname = ((ASTIdentifier)mixins.get(i)).getName();
+ String iname = mixinname + "$";
+ if (superiname != null)
+ iname += superiname;
+ mixinRef.put(iname, new MixinReference(mixinname, superiname));
+ superiname = iname;
+ }
+ children[2] = newIdentifier(superiname);
+ children[3] = newIdentifier(null);
+ }
boolean savedInDefault = inDefaultClass;
inDefaultClass = false;
try {
- translateClassDirectivesBlock(dirs, classnameString, props, classProps,
stmts);
+ // Both classes and mixins are translated to create a normal
+ // 'implementation' class. A mixin's implementation is stashed
+ // into the mixinDef hashmap and is used to create interstitials
+ // for use by any mixin client. But the mixin's implementation
+ // will not appear directly in the parse tree. However, a mixin
+ // is translated a second time here to create its interface,
+ // and that's the part that remains in the parse tree and gets
+ // emitted.
+ //
+ if (!isClass) {
+ // before visiting, create a shallow copy of the mixin for the
interface
+ mixinInterface = new ASTClassDefinition(0);
+ for (int i=0; i<children.length; i++) {
+ mixinInterface.set(i, children[i]);
+ }
+ }
+ translateClassDefinition(node, classnameString, TranslateHow.AS_CLASS);
- /*
- * TODO: [2007-12-11 dda] do anything with props/classProps?
- */
-
- // Plug the stmt children we found back into this node's children
- SimpleNode[] newch = new SimpleNode[stmts.size() + 4];
- int i;
- for (i=0; i<4; i++)
- newch[i] = children[i];
- for (Iterator iter = stmts.iterator(); iter.hasNext(); ) {
- SimpleNode n = (SimpleNode)iter.next();
- newch[i++] = visitStatement(n);
+ if (!isClass) {
+ mixinDef.put(classnameString, node);
+ node = mixinInterface; // the interface will appear in the final tree
+ translateClassDefinition(node, classnameString,
TranslateHow.AS_INTERFACE);
}
- node.setChildren(newch);
- visitChildren(node);
}
finally {
- options.putBoolean(Compiler.KEEP_CLASS_METHODS, false);
inDefaultClass = savedInDefault;
}
return node;
}
+ public void translateClassDefinition(SimpleNode node, String
classnameString, TranslateHow how)
+ {
+ SimpleNode[] children = node.getChildren();
+ SimpleNode[] dirs = (SimpleNode [])(Arrays.asList(children).subList(4,
children.length).toArray(new SimpleNode[0]));
+ List stmts = new ArrayList();
+
+ translateClassDirectivesBlock(dirs, classnameString, null, null, stmts,
how);
+
+ // Plug the stmt children we found back into this node's children
+ SimpleNode[] newch = new SimpleNode[stmts.size() + 4];
+ int i;
+ for (i=0; i<4; i++)
+ newch[i] = children[i];
+ for (Iterator iter = stmts.iterator(); iter.hasNext(); ) {
+ SimpleNode n = (SimpleNode)iter.next();
+ newch[i++] = visitStatement(n);
+ }
+ node.setChildren(newch);
+ visitChildren(node);
+ }
+
/**
* Intercept JavascriptGenerator version.
* SWF9 does super calls 'normally' just by keeping the super keyword.
@@ -232,9 +326,59 @@
*/
public SimpleNode translate(SimpleNode program) {
savedProgram = program;
- return super.translate(program);
+ SimpleNode result = super.translate(program);
+
+ // Walk list of needed interstitials and emit them
+ for (Iterator iter = mixinRef.keySet().iterator(); iter.hasNext(); ) {
+ String isname = (String)iter.next();
+ MixinReference ref = (MixinReference)mixinRef.get(isname);
+ SimpleNode mixin = (SimpleNode)mixinDef.get(ref.mixinname);
+ if (mixin == null) {
+ throw new CompilerError("Missing definition for mixin: " +
ref.mixinname);
+ }
+ result.set(result.size(), createInterstitial(mixin, isname,
ref.mixinname, ref.supername));
+ }
+ return result;
}
+ SimpleNode newIdentifier(String name)
+ {
+ if (name == null) {
+ return new ASTEmptyExpression(0);
+ }
+ ASTIdentifier id = new ASTIdentifier(0);
+ id.setName(name);
+ return id;
+ }
+
+ SimpleNode createInterstitial(SimpleNode mixin, String isname, String
mixinname, String superisname)
+ {
+ // At this point, the mixin has already been visited.
+ // We just need to grab its contents and make a fresh class
+ // out of it.
+
+ SimpleNode[] mixinchildren = mixin.getChildren();
+ SimpleNode[] ischildren = new SimpleNode[mixinchildren.length];
+ SimpleNode isnode = new ASTClassDefinition(0);
+
+ ischildren[0] = newIdentifier("class");
+ ischildren[1] = newIdentifier(isname);
+ ischildren[2] = newIdentifier(superisname);
+
+ // Before the tree is visited, child[3] has a list of
+ // 'with'/'inherits' - that is, names of mixins.
+ // After the tree has been visited, child[3] has an
+ // identifier, which is the name of an interface,
+ // something that the class 'implements'.
+ // For an interstitial, that is the name of the mixin.
+
+ ischildren[3] = newIdentifier(mixinname);
+ System.arraycopy(mixinchildren, 4, ischildren, 4, mixinchildren.length -
4);
+ isnode.setChildren(ischildren);
+
+ return isnode;
+ }
+
public String preProcess(String source) {
// TODO: [2007-1-14 dda] maybe tag compiler should emit app main class?
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9ParseTreePrinter.java
===================================================================
---
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9ParseTreePrinter.java
2008-02-09 19:28:12 UTC (rev 7990)
+++
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9ParseTreePrinter.java
2008-02-09 19:29:03 UTC (rev 7991)
@@ -55,6 +55,9 @@
/** True if we are creating a shared library */
private boolean islib;
+ /** State variable that is true while we are in a mixin */
+ private boolean inmixin = false;
+
// Adjust the known operator names we output to include
// ones that we know about.
static {
@@ -81,6 +84,25 @@
this.islib = sharedLibrary;
}
+ /**
+ * Intercept parent.
+ *
+ * We need to know if we are inside a mixin, since it changes how we
+ * emit its contents. This must be noticed in previsit rather than in
+ * visitClassDefinition because it's already too late there - the
+ * unparser works bottom up. At the moment we do not allow nested
+ * classes/mixins, so we can keep a single state variable.
+ */
+ public SimpleNode previsit(SimpleNode node) {
+ if (node instanceof ASTClassDefinition) {
+ ASTIdentifier id = (ASTIdentifier)(node.getChildren()[0]);
+ if (!"class".equals(id.getName())) {
+ this.inmixin = true;
+ }
+ }
+ return super.previsit(node);
+ }
+
// Override parent class.
//
// We need a translation unit (class) to put all the executable
@@ -200,12 +222,20 @@
// Everything inserted by #passthrough (toplevel:true) goes here.
sb.append(annotateInsertStream(TOP_LEVEL_STREAM));
- sb.append("class" + SPACE + classnm + SPACE);
+ if ("class".equals(unannotate(children[0]))) {
+ sb.append("class");
+ }
+ else {
+ sb.append("interface");
+ }
+ sb.append(SPACE + classnm + SPACE);
if (unannotate(children[2]).length() > 0)
sb.append("extends" + SPACE + children[2] + SPACE);
- // TODO: [2007-11-20 dda] handle 'with' or inherits clause
- // if (unannotate(children[3]).length() > 0) ....
+ // The meaning of children[3] is 'implements'
+ if (unannotate(children[3]).length() > 0) {
+ sb.append("implements" + SPACE + children[3] + SPACE);
+ }
sb.append("{\n");
sb.append(annotateInsertStream(CLASS_LEVEL_STREAM));
@@ -219,9 +249,17 @@
}
sb.append("}\n");
+ // Note: assumes no nested mixins
+ inmixin = false;
+
return annotateClass(classnm, sb.toString());
}
+ // override - don't emit body of methods for mixins
+ public String visitFunctionDeclaration(SimpleNode node, String[] children) {
+ return doFunctionDeclaration(node, children, true, inmixin);
+ }
+
public String visitPragmaDirective(SimpleNode node, String[] children) {
return "// (ignored) pragma " + children[0];
}
@@ -235,7 +273,7 @@
public String visitIdentifier(SimpleNode node, String[] children) {
ASTIdentifier ident = (ASTIdentifier)node;
- String name = ident.getName();
+ String name = super.visitIdentifier(node, children);
String type = ident.getType() == null ? "" : (":" + ident.getType());
String ellipsis = ident.getEllipsis() ? "..." : "";
_______________________________________________
Laszlo-checkins mailing list
[email protected]
http://www.openlaszlo.org/mailman/listinfo/laszlo-checkins