// repetition.beh -- Repetition conversion // $Id: repetition.beh,v 1.4 2000/10/13 21:04:59 dougo Exp $ // What we want to do is convert the repetition class definition // // Foo_List ~ "a" Foo { (@ lookahead @) "b" Foo "c" } "d". // // into two construction class definitions, a container // // Foo_List = "a" Nonempty_Foo_List "d". // // and a link node // // Nonempty_Foo_List = Foo // [ (@ lookahead @) "b" Nonempty_Foo_List "c" ]. // // If the repetition class is possibly empty (i.e. Foo_List ~ { Foo }), // then the part of the new Foo_List class is optional, and // the lookahead is added in this optional part as well. // Geoff Hulten 4/15/97: // Changed this to create a hashtable 'repdict' that contains the // name of all the repition classes as it does the repition conversion. // This is needed by the displayAsTree visitor generator. // Johan May 19: Added enumerationish methods to all repetition classes. // Foo_List = "a" Nonempty_Foo_List "d". // is not transformed into // Foo_List = "a" Nonempty_Foo_List "d" [ Nonempty_Foo_list]. // but something like it, at the java level (so no traversals go through !) // I needed to be able to figure out what the element type was. I finessed repdict to // store this info as well. Program { (@ Dictionary repdict; @) /** Convert all repetition classes to (pairs of) construction classes with recursive edges. */ private void convertRepetition() via RepetitionClass bypassing -> *,nonempty,* to { PlainSyntax, PrintIndent, PrintUnindent, PrintSkip, PrintSpace, ClassName } { (@ Hashtable repdict = new Hashtable(); ClassName classname; String container, linknode, linknodename; boolean inRepeatedPart; @) before ClassDef (@ classname = host.get_classname(); container = null; @) before RepetitionClass (@ container = "= "; linknodename = RepetitionClass.makeLinknodeName(classname); @) before Syntax (@ if (inRepeatedPart) linknode += host + " "; else container += host + " "; @) before RepeatedPart (@ String first = " " + linknodename; inRepeatedPart = true; linknode = "[ "; LocalLookahead l = host.get_locallookahead(); if (l != null) { linknode += l + " "; } if (host.get_nonempty() == null) { // The repetition is possibly empty; make the link node optional. if (l != null) { first = l + " " + first; } first = "[ " + first + " ] "; } container += first; @) before ClassName (@ repdict.put(classname, host); linknode = "= " + host + " " + linknode + " " + linknodename + " "; @) after RepeatedPart (@ linknode += "]."; inRepeatedPart = false; @) after RepetitionClass (@ container += "."; @) after ClassDef (@ if (container != null) { if (Program.dbg) { System.err.println(container); } // Replace the repetition class with the new container class. host.set_classparts(ConstructionClass.parse(container)); // Add the class definition for the new link node class. ClassDef linknodedef = host.deepCopy(); linknodedef.set_paramclassname(ParamClassName.parse(linknodename)); linknodedef.set_classparts(ConstructionClass.parse(linknode)); Program.prog.addClassDef(linknodedef); } @) after Program (@ host.repdict = repdict; @) } } RepetitionClass { static String makeLinknodeName(ClassName classname) (@ return "Nonempty_" + classname; @) } ClassName { /** Are you or have you ever been a member of the RepetitionClass class? */ boolean isOrWasRepetitionClass() (@ ClassDef def; return wasRepetitionClass() || ((def = Program.prog.findClassDef(this)) != null && def.isRepetitionClass()); @) /** Was this class a repetition class that was converted? */ boolean wasRepetitionClass() (@ return Program.prog.repdict.get(this) != null; @) } // enumeration stuff: // Makes assumptions about naming of repetition class parts! // Must be called after traversal methods are generated! Program { public void addEnumerationStuff() = allClassDefs { before ClassDef (@ ClassName cn = host.get_classname(); if (!cn.isOrWasRepetitionClass()) return; // if we get to here, host is (was) a repetition class. // add some methods ClassName ecn = (ClassName) Program.prog.repdict.get(cn); String ln = RepetitionClass.makeLinknodeName(cn); host.addBehavior(ClassMethods.parse( "{\n"+Text.begin+"\n" +" private " + ln + " tail;\n" +" public void addElement(" + ecn + " e) { \n" +" checktail(); \n" +" if (tail == null) {\n" +" first = new " + ln + "(e,null); tail = first;\n" +" } else {\n" +" tail.set_next(new " + ln + "(e,null)); tail = tail.get_next();\n" +" }\n" +" }\n\n" +" public void push(" + ecn + " e) { first = new " + ln + "(e,first); }\n\n" +" public java.util.Enumeration elements() { return new "+cn+"(first); } \n\n" +" public int size() {\n" +" int i= 0;\n" +" for (java.util.Enumeration e=elements(); e.hasMoreElements(); i++)\n" +" e.nextElement();\n" +" return i;\n" +" }\n" +" public boolean isEmpty() { return (first == null); }\n\n" +" public boolean hasMoreElements() { return (first != null); }\n\n" +" public Object nextElement() {\n" +" " + ecn + " car = first.get_it();\n" +" first = first.get_next();\n" +" return (Object) car;\n" +" }\n\n" +" private void checktail() {\n" +" if (tail == null && first != null) {\n" +" tail = first;\n" +" while (tail.get_next() != null) tail = tail.get_next();\n" +" }\n" +" }\n" +" public boolean contains("+ecn+" e) {\n" +" java.util.Enumeration en = this.elements();\n" +" while (en.hasMoreElements())\n" +" if (e.equals(("+ecn+") en.nextElement())) return true;\n" +" return false;\n" +" }\n" +Text.end+"}")); host.addInterface(Interface.parse("java.util.Enumeration.")); @) } } ClassDef { void addInterface(Interface i) to Interface_Commalist { before ClassParents (@ if (host.get_interfaces() == null) host.set_interfaces(new Interfaces( new ImplementsKeywordWithoutStars(), new Interface_Commalist())); @) before Interface_Commalist (@ host.addElement(i); @) } }