-*- mode:outline; minor-mode:outl-mouse -*- * Since 0.6.2: ** init methods are public Init methods (no-arg constructors) are now public by default (they were previously package scope). ** bug fix: parser rules for terminal classes Parser rules for terminal classes inside not-parsed visitors (e.g. inline adaptive methods) were being erroneously generated, which caused compile errors if the terminal class had no no-arg constructor (e.g. PrintWriter). This no longer happens. ** bug fix: importing a specific class Import statements in a CD of the form "import foo.bar.Baz;" now work correctly. ** performance: use Adler32 instead of CRC32 Checksums computed when generating code now use the Adler32 algorithm instead of CRC32, which should make it a bit faster. * Since 0.6.1: ** interfaces You can now define interface classes in your .cd file, just like regular classes: *interface* Foo : Bar | Baz. *interface* Bar = . *interface* Baz = . Currently you can't define parts on an interface. You can define interface methods in a .beh file: Bar { int quux(String s); void garply(Baz b, int x); } You cannot traverse to an interface, nor can you put wrappers on an interface-- they are treated just like terminal classes. ** abstract methods You can now define abstract methods in a .beh file: Foo { abstract void bar(int x); } You can only define abstract methods on alternation classes. ** alternation classes need not have alternatives You can define an alternation class without any alternatives: Foo : . This defines an abstract class, which cannot be parsed. ** package-qualified names in the class dictionary You may now refer to fully package-qualified names in the class dictionary (e.g. "Foo = java.util.Vector"). If no part name is provided, the generated part name will be the base class name converted to lowercase (e.g. "" in the above example). ** import statements in class dictionary You may now specify import statements at the top of the class dictionary, below the package declaration (if any). Both forms are allowed: import java.util.Vector; // import one class import java.io.*; // import all classes in the package ** new *visitors* keyword You may flag a section (or multiple sections) of the class dictionary as being visitor classes by surrounding it with "*visitors*" and "*endvisitors*". Or, for a single class, use the keyword "*visitor*" before the class name. Any class so marked will automatically extend UniversalVisitor (unless it already extends something else); thus you can write traversal methods that take UniversalVisitor arguments and pass them any visitor class. ** alternate syntax for adaptive methods You may use any of the following syntaxes for defining an adaptive method using an existing traversal method and visitor classes: // assume "trav" is a traversal method with args "Vis1" and "Vis2" void m1() trav(Vis1,Vis2); // old syntax void m2() = trav(Vis1,Vis2); // Josh's preference void m3() = trav with Vis1,Vis2; // Doug's preference Two of these are likely to go away soon... ** return value in adaptive methods need not declare return type For an "inline" adaptive method, a return value method need not declare a return type; it will be the same as the adaptive method's return type. For example, you can say: int f() to X { return (@ 3 @) } rather than: int f() to X { return int (@ 3 @) } However, visitor classes defined outside of adaptive methods must still specify the return type. ** -tie option to tie stderr to stdout For broken command shells that don't let you redirect the standard error stream (e.g. DOS 7 in Windows 95), the "-tie" command-line option can be used to send all error messages to standard output. ** performance: around methods no longer use inner classes To avoid creating huge number of .class files, around methods have been reimplemented using the reflection package instead of with inner classes. ** bug fix: class with init method and no parts A class with an init method and no parts now compiles without error. ** bug fix: using existing traversal methods with inline visitor in adaptive methods You can now use an existing traversal method with an inline visitor in an adaptive method; the generated visitor will extend the traversal method's parameter class. The traversal method must have exactly one parameter, though. ** bug fix: adaptive method with multiple sources An adaptive method attached to multiple source classes now compiles correctly. E.g. { Foo, Bar } { void f() to Baz { before Baz (@ ... @) } } will generate two traversal methods and two visitors. (Probably should be optimized to one visitor...) * Since 0.6: ** automatic return_val variable in adaptive methods An adaptive method with inline behavior without a return value expression will have an implicit variable "return_val" whose type is the return type of the adaptive method. For example: A { B getB() to B { before B (@ return_val = host; @) } } ** bug fix: empty behavior list in adaptive method This no longer causes an exception: A { void foo() to B {} } ** bug fix: multiple adaptive methods on a class There may now be more than one adaptive method per class. ** better diagnostic for traversal calculation A warning is printed for each source and target of a traversal that isn't in the calculated traversal graph. ** "No path found" error less restrictive Traversal graph calculation only halts with an error if none of the sources or none of the targets of a traversal are in the traversal graph. * Since 0.5.1: ** traversals as objects Traversals as Objects allows the programmer to manipulate traversals (instances of the class Traversal) as any other object. This technology is enabled with the -tao switch. The -debug switch gives [very] verbose debugging info. To make a traversal, use the static fromString method: Traversal t1 = Traversal.fromString("from A to {B,C};"); Traversal t2 = Traversal.fromString("from {A,B} to C then to {D,E};"); Traversal t3 = Traversal.fromString("from {A,B} bypassing D to C;"); Then call a traversal on an instance object. t1.traverse(instobj,some_visitor); instobj must be one of the classnames given as a from class. some_visitor is either a Vector, array or single instance of a Visitor. All visitors used must be a subclass of TAOVisitor. TAOVisitor must be in the .cd file. The current implemention is naive, so there are some constant factors that can be shaved off both runtime and compile time, but I don't think better than 1/3 the performance of static traversals is achievable. This may be optimistic. Known bugs: Edges are ignored. They cannot be specified in the traversal, and their wrappers will not be called. The * classname glob is not [yet] implemented. Thanks to Johan Ovlinger for the TAO implementation. ** method signatures can be declared outside (@ @) The following syntax for a method in a behavior file is now accepted: Foo { boolean bar(Baz b, Fred x[], int[][] j) (@ // arbitrary java code here @) } The old style of putting the signature inside the (@ @) is still supported, and still necessary in certain cases (e.g. constructors, or methods that throw static exceptions) but this new style of declaring methods is encouraged. ** return values for visitors A visitor class may declare a return value expression, for example: FooVisitor { return int (@ x + y @) } Inside (@ @) can be any valid Java expression of the type declared. This is useful for visitors used in adaptive methods (see below). ** traversal parameter names no longer need to be specified Instead of saying this: traversal t(FooVisitor f, BarVisitor b) { ... } you can just list the argument types (visitor classes): traversal t(FooVisitor, BarVisitor) { ... } since the variables aren't accessible to user code anyway. ** adaptive methods An adaptive method is a combination of a traversal method with one or more visitor classes: program.cd: Foo = Bar. Bar = int Baz. Baz = String. BarBazVisitor : BarBazCollector. BarBazCollector = String int. program.beh: Foo { traversal bazInBar(BarBazVisitor) { via Bar to Baz; } } BarBazVisitor { before { Bar, Baz } (@ @) return String (@ "" @) } BarBazCollector { before Bar (@ if (host.get_i() > min) s += "i > min"; @) before Baz (@ s += host.get_s(); @) return String (@ s + "\n" @) } Foo { // Adaptive method combining a traversal with a visitor. String collectBarBaz(String s, int min) bazInBar(BarBazCollector); } The above adaptive method gets translated (roughly) to: Foo { String collectBarBaz(String s, int min) BarBazCollector v = new BarBazCollector(); v.set_s(s); v.set_min(min); bazInBar(v); return v.get_return_val(); @) } The visitor class that is specified in the adaptive method must have parts matching the parameters in the adaptive method signature, and a return method (see above) matching the return type (unless it's void). An adaptive method may specify a list of visitor classes to be combined with a traversal method: Foo { traversal t(V1, V2, V3); int f(float x, boolean b) t(V1, V2, V3); } However, the adaptive method parameters must exist as parts on the first visitor, and only the first visitor's return value is returned from the adaptive method. (A future enhancement may provide a more general way to initialize a set of visitor objects and compute the return value from them.) Either or both of the traversal and visitor may be specified "inline" when defining an adaptive method. For instance, the above adaptive method, traversal, and visitor could all be specified with a single adaptive method definition: Foo { String collectBarBaz(String s, int min) via Bar to Baz // the traversal strategy { // the set of visitor methods before Bar (@ if (host.get_i() > min) s += "i > min"; @) before Baz (@ s += host; @) return String (@ s + "\n" @) } } The arguments to the adaptive method automatically become parts of the new (implicitly generated) visitor class. ** *lookahead* keyword Parser lookahead directives can be specified at choice points in the class dictionary grammar with "*lookahead* (@ @)". A choice point is one of: * An alternation: Foo : *lookahead* (@ 2 @) Bar | Baz. * An optional part: Foo = [ *lookahead* (@ 2 @) Bar ]. * A repetition part: Foo ~ { *lookahead* (@ 2 @) Bar }. A lookahead directive is one of: * An integer: the number of tokens to look ahead. (default is 1) * A parser rule or set of rules: each class Foo has a rule _Foo(); a lookahead directive (@ _Foo() _Bar() @) means to look ahead until Foo and Bar have been matched. * An arbitrary boolean Java expression, inside curly brackets. See the file examples/Lookahead/README in the JavaCC distribution for more detail on how this works and when it is needed. Thanks to Binoy Samuel for lookahead directives. ** Repetitions have contains(Foo) method. It uses .equals() to check if an element is in the collection. ** fixed isEmpty() on repetition classes The generated isEmpty() method on repetition classes now returns the right answer. ** LongGetters removed. Getting them right in all cases turned out to be a lot of work; with adaptive methods availible, I'm not willing to do it. * Since 0.5: ** selective parser code generation There are now several ways to turn off generation of parser code for classes. To turn it off for an individual class, use: Foo *noparse* = ... To turn it off for a section of the class dictionary: *noparse* Foo = ... Bar = ... *parse* To turn it on for an individual class inside a section of classes in which it's turned off: *noparse* Foo = ... Bar *parse* = ... Baz = ... *parse* In any case, when parser code generation is turned off, no .parse() method will be defined for the class, and no parsing rule in the grammar will be generated. This means that a parsed class with a part that's a non-parsed class will cause a compile-time error. The generated PrintVisitor and DisplayVisitor still include non-parsed classes, so specifying syntax may still be useful for these. ** wrapper method lookup optimizations Traversal method expansion is now much faster, especially for visitors with a large number of wrappers (e.g. the generic visitors). * Since 0.4.8: ** general traversal strategies A strategy expression may be a path or a general graph. Here's the currently supported syntax for strategies. It was pretty much hacked up from nowhere, so it's likely to change, especially the general graph form. Other syntaxes will be added, e.g. strategy variables and compound strategies (join, merge, intersect). Note that the current traversal syntax is still supported by this syntax. Note also that everywhere you can specify a single class or edge, you can also specify a curly-bracketed set of classes or edges. And finally, "through " is not yet supported, but "through " and "only-through " are both supported, so you can translate it by hand for now (see example below). *** Syntax: // Same syntax as before; a traversal is a strategy attached to a // class with a name and list of formal parameters. Traversal = "traversal" TraversalName TraversalParms "{" StrategyExpression ";" "}". TraversalParms = "(" [ Commalist(Visitor) ] ")". Visitor = ClassName VisitorName. // Strategies can be lines or graphs. StrategyExpression : PathDirective | StrategyGraph. // Line strategies: PathDirective = [ NegativeConstraint ] List(PathSegment) TargetDirective. PathSegment = PositiveConstraint [ NegativeConstraint ]. TargetDirective : To | ToStop *common* ClassGlobSpec. To = "to". ToStop = "to-stop". // General graphs: StrategyGraph = "{" List(SGEdge) "}" [ "source:" ClassGlobSpec ] [ "source-edge:" NList(Integer) ] // indices of source edges [ "target:" ClassGlobSpec ]. SGEdge = ClassGlobSpec "->" ClassGlobSpec [ NegativeConstraint ]. // Constraints: Constraint : PositiveConstraint | NegativeConstraint *common* GlobSpec. PositiveConstraint : Through | Via. // These are synonyms. Through = "through". Via = "via". NegativeConstraint : Bypassing | OnlyThrough. // These are antonyms. Bypassing = "bypassing". OnlyThrough = "only-through". // Globs -- nothing's changed here. GlobSpec : OneGlob | GlobSet. OneGlob = Glob. GlobSet = "{" [ Commalist(Glob) ] "}". ClassGlobSpec : OneClassGlob | ClassGlobSet. OneClassGlob = ClassGlob. ClassGlobSet = "{" [ Commalist(ClassGlob) ] "}". Glob : ClassGlob | EdgeGlob. EdgeGlob : PartGlob | SubclassGlob | SuperclassGlob. ClassGlob = ClassNameGlob. PartGlob = "->" SourceGlob "," PartNameGlob "," DestGlob. SubclassGlob = "=>" SourceGlob "," DestGlob. SuperclassGlob = ":>" SourceGlob "," DestGlob. SourceGlob = ClassNameGlob. DestGlob = ClassNameGlob. ClassNameGlob : ClassNameExact | AnyClass. ClassNameExact = ClassName. AnyClass = "*". PartNameGlob : PartNameExact | AnyPart. PartNameExact = PartName. AnyPart = "*". // Parameterized classes. List(S) ~ { S }. NList(S) ~ S { S }. // Terminal buffer classes. ClassName = Ident. PartName = Ident. TraversalName = Ident. VisitorName = Ident. *** Some examples: via A to B; through A to B; // synonym for above via { A, B, C } bypassing -> *,x,* to { D, E, F }; via A via B via C to D; via A only-through -> A,b,B via B to C; // same as "through -> A,b,B to C". { A -> B bypassing X B -> C C -> A } source: A target: C; { A -> B B -> A A -> C } source-edge: 0 target: C; // same as "via B via A to C" attached to A. ** new methods on repetition classes Repetition classes now define the additional methods void push(...) -- add an element to the front of the list boolean isEmpty() -- is the list empty? ** multiple targets & common parts bug fixed A long-standing bug relating to multiple targets in a traversal over an alternation class with common parts has been fixed. ** .xcd bug fixed The expanded class dictionary printed to program.xcd no longer includes the dump of the classdef hashtable. ** deprecated JavaCC syntax removed The generated grammar.jj file now compiles without warning by JavaCC-0.7pre4. * Since 0.4.7: ** new subgraph calculation code Subgraph calculation (i.e. traversal expansion) is now much faster. ** parse(String) catches ParseError The parse(String) method generated for each class now throws a RuntimeException rather than a ParseError, to avoid having to wrap every call in a try/catch block. The InputStream version still throws ParseError. ** less verbose output Some messages are coalesced from many lines into one line. ** log messages to stdout instead of stderr All non-error messages are printed to the standard output stream, rather than the standard error stream. ** PrintVisitor is smarter about spaces The generated PrintVisitor now only prints spaces if it needs to, rather than after every object. It will print a space whenever it is needed to disambiguate between syntax and identifiers, e.g.: program.cd: Foo = "x" Ident "x" "(" Ident ")". program.beh: Ident f = new Ident("foo"); Foo foo = new Foo(f, f); foo.universal_trv0(new PrintVisitor(System.out)); This will print "x foo x(foo)" rather than "x foo x ( foo )". To print extra spaces, use the "*s" syntax directive, e.g. Commalist(X) ~ X { "," *s X } ** PrintVisitor constructor for PrintStream A PrintVisitor object can be constructed from either a PrintWriter or a PrintStream, because even in JDK 1.1, System.out is a PrintStream. ** expanded class dictionary is saved After parsing "program.cd", the expanded class dictionary is written to "gen/program.xcd". This CD has no parameterized classes or repetition classes, and all part names and inheritance edges are explicitly specified. E.g. A : B | C *common* List(D). B = . C = . D = . List(X) ~ { X }. becomes A : B | C *common* D_List. B = *extends* A. C = *extends* C. D = . D_List = [ D ]. Nonempty_D_List = D Nonempty_D_List. Note that the class dictionary is *not* flattened. Also, repetition class expansion is likely to change (or go away entirely). ** repetition classes use java.util.Enumeration instead of Enumeration Class dictionaries with repetition classes are no longer required to import java.util.Enumeration. * Since 0.4.6: ** PrintVisitor uses PrintWriter The generated PrintVisitor constructor now takes a PrintWriter instead of a PrintStream, to be compatible with JDK 1.1. ** parts are protected instead of private Parts defined by the class dictionary are now generated as protected parts instead of private. This is mainly to make visitor inheritance less painful. ** Long_Getters To avoid gratuitous breaking of LoD in Johan's programs, I've added long_getters. If you have a long chain of get_far().get_foo().get_bar().get_blox(), this can be replaced by a single long_get_blox(). Of course, you might not get the one you want. Then you need to use a 'through' like concatenation: long_get_foo().long_get_blox(). The object that gets returned by a long_getter is (should be) the first part that is found by a depth first search of the parts reachable from a class. Superclasses, and parts reachable from them come last. Ie, the part that is returned is the first such part that would have been encountered by a traversal to that part. NullPointerException is thrown if an optional part fails to exist. I attempted to do some error handling before, but the interaction with primitive types is nasty (basically, int and null are disjoint. Yuck). Think of a better solution? look in gengetters.beh. I generate getters for ALL reachable parts. Code explosion? I don't think so, but the switch -no_long_get turns off their generation. ** Enumeration thingies Repetition classes now define the methods void addElement(...), Enumeration elements(), nextElement(), hasMoreElements. They behave as expected. Apparently, Enumeration must be imported by the application. Perhaps we can fix this, when interfaces can contain "."s. There is a tentative fix and a nicer version that requires itself to compile commented out in repetition.beh. Please use that for 0.4.8. ** Around methods "Around" methods are methods on a visitor class that control when and how a subtraversal is continued. For example: Visitor { around Foo (@ if (some_condition) subtraversal.apply(); @) } A traversal through a Foo using a Visitor will only continue to its subobjects if "some_condition" is true. The variable "subtraversal" is a reference to an object of an unspecified class that defines one method, void apply(), which performs the continuation of the current traversal. Any "before" and "after" methods for Foo would also be called as normal, before and after the "around" method. An "around" method may be attached to anything "before" and "after" methods may be attached to, including an edge or a set of classes or edges. For more information, see http://www.ccs.neu.edu/home/lth/aao/impl.html. Thanks to Lars Hansen for the design and implementation of this feature. ** Derived parts These are parts that aren't represented in the structure of an object, but are instead computed as they are needed. You specify that a part should be derived by providing a 'get' or a 'set' method for the part in one of the program's .beh files. You specify how a derived part is ccomputed by providing a 'get' method in one of your program's .beh files (some parts of demjava require you to specify a get method for every derived part...this may change in a future version). eg: In .cd: Class = Derived_Part . In .beh: Class { get derived_part (@ return new Derived_Part(...); @) } A get method simply provides a getter for the part, so a call to get_derived_part() would invoke it to get the part. You can also specify a 'set' method that will be called when set_derived_part() is called. In the set method the new object (in the example an object of class Derived_Part) will be bound to 'dest'. ** GNUmakefile.template changes to support derived methods Behavior files may now influence grammar generation, so there is no reason to generate code and grammar in separate passes. ** Display and Trace generic visitors Two more generic visitors: -displayvis ==> generates DisplayVisitor -tracevis ==> generates TraceVisitor (see the next changes section for a bit more info about generic visitors). "new DisplayVisitor(PrintStream out)" creates a visitor that displays the parts of the object being traversed as a tree. "new TraceVisitor(PrintStream out)" creates a visitor that traces before and after methods for every class and edge a traversal visits. ** fixed bug w/ universal traversal and extending external classes The universal traversal generated in relation to the generic visitors was producing bad code for a class that extended an external class, e.g. MyStack = *extends* Stack. This should now compile correctly with generic visitors. * Since 0.4.5: ** generic visitors and the universal traversal Three generic visitors can be generated: -printvis ==> generates PrintVisitor -copyvis ==> generates CopyVisitor -equalvis ==> generates EqualVisitor If any of these are generated, an abstract UniversalVisitor will also be generated. To generate just the UniversalVisitor, use -univis. "new PrintVisitor(PrintStream out)" creates a visitor that prints syntax for every class and edge in the traversal. "new CopyVisitor(Class cl)" creates a visitor that copies every object in the traversal. Terminals are copies by reference. "cl" is the class of the root object. Use "get_copy()" to retrieve the copy after traversal. "new EqualVisitor(Object obj)" creates a visitor that compares every edge in the traversal with every edge in obj. Use "get_is_equal()" to retrieve the equality boolean after traversal. In addition, a special universal traversal named "universal_trv0" is generated that traverses every construction edge in every class. Its argument is a UniversalVisitor. Thanks to Geoff Hulten for these three visitors. ** terminal edge wrappers get called Wrappers on construction edges to terminal classes are invoked. For example, before -> Foo,x,int (@ total += dest; @) These are currently only generated in "to *" traversals. ** traversal methods have package scope Generated traversal methods of the form "foo_trv3" have package scope instead of public. Only the entry points are publicly accessible. ** "this." to prevent name clashes Generated traversal methods use "this." to refer to parts, to prevent conflicts with visitor argument names. ** Text.begin, Text.end, Text.quoted_end Final static String members on Text: begin = "(@" end = "@)" quoted_end = "\"@\" + \")\"" * Since 0.4.4: ** multiple behavior files The command line argument interface has changed slightly. Instead of demjava program use demjava program.cd program.beh You can also have more than one behavior file, e.g. demjava program.cd program1.beh program2.beh program3.beh The behavior files will simply be concatenated before compiling. ** better file error handling A nicer error message is printed if any of the input files do not exist or are unreadable, or if the output directory is not writable. Also, if the output directory does not exist, it will be created automatically. ** compatibility with JavaCC The generated grammar file is called "grammar.jj" to comply with JavaCC's ".jj" naming convention. ** GNUmakefile.template changes Various changes to support the above three changes. ** init methods In the behavior file, you can specify an init method for a class: Foo { init (@ System.out.println("new Foo"); @) } This becomes a constructor with no arguments. The constructor has default scope (i.e. package). ** multiple construction edge wrappers You may now have multiple construction edge wrappers with the same source and dest in a single visitor, e.g. Visitor { before -> *,a,* (@ ... @) before -> *,b,* (@ ... @) } Previously, this didn't work because both were generated as before(Object source, String edge, Object dest) Now, they are generated as before__a(Object source, Object dest) before__b(Object source, Object dest) Note the "edge" argument is now only available with construction edge wrappers that use a wildcard for the part name. ** JDK 1.1 compatibility The use of StringBufferInputStream for parsing from strings caused deprecation warnings with javac 1.1. Instead, we now use ByteArrayInputStream. (We can't use StringReader because JavaCC doesn't yet support Readers.) Also, demjava now uses PrintWriter internally, so it will not work without 1.1 classes. However, demjava-generated code will still work with 1.0.2. * Since 0.4.3: ** bypassing classes When a traversal bypasses a class, it now bypasses all incoming edges to that class. Previously it was bypassing outgoing edges, which was incorrect. ** fixed wrapper call bugs In some traversals involving alternation classes, a wrapper method (before or after) was being called multiple times, being called in the wrong order, or not being called at all. This no longer happens: each wrapper method on an object's class or ancestors is called once for each object, and the order in which they are called obeys the prefix-super-sub and suffix-sub-super rules (see p. 258 in the Demeter book). ** prematurely terminated paths A traversal that enters an object whose class is not in the path will now be terminated, rather than continuing. ** fixed file descriptor leak Running demjava for the second time on a large cd file would cause demjava to run out of file descriptors, because it wasn't closing the diff streams. It now closes them. * Since 0.4.2: ** part initializers A class part (in a .cd file) may have a default initializer using the following syntax: float *init* (@ 23.45 @) The text inside (@ @) may be any Java expression that can be used as an initializer for a data member of the given type. ** one .java file per class Rather than create one large program.java file, a classname.java file is created for every class in program.cd. This follows the Java standard style, and is required for public classes (and even package classes, with javac 1.1). ** timestamp preservation For every output file, if there already exists a file of the same name with the exact same contents, demjava does not overwrite the old file, so as to preserve the timestamp for the benefit of programs like make. ** new -outputdir command-line option Specifies where to put the output files. Default is the gen/ directory. ** GNUmakefile.template changes The GNUmakefile.template (which is copied into the user's directory with j-gen-make) now supports the above three features to minimize recompilation. ** class keywords *public* and *final* A class definition (in a .cd file) may be preceded by the tokens *public* and/or *final*; these have the same meaning as they do in Java. Just don't try to make an alternation class *final*! ** part keywords *static*, *final*, *read-only*, and *private* A class part (in a .cd file) may be preceded by the tokens *static*, *final*, *read-only*, and *private* (before the part class, but after the part name, e.g. *static* Foo). *static* and *final* have the same meaning as they do in Java; *read-only* means that no mutator (set_foo) function will be generated, and *private* means that no accessor (set_foo and get_foo) functions will be generated. [Actually, they are generated, but they have package scope rather than public.] ** new -version command-line option Prints the current version number. ** nonzero return code on error When demjava exits with an error, the return code is nonzero, for the benefit of programs like make. ** improved error handling Top-level syntax errors in .cd or .beh files are now reported. (Accomplished via the *EOF* token in demjava.cd -- see below.) ** new *EOF* token A class definition may end with the *EOF* token. This causes the parser to ensure that the object sentence ends after parsing this class, throwing a ParseError otherwise. For example, Foo = "foo" *EOF* . Foo.parse("foo bar") will cause a ParseError to be thrown, rather than quietly succeeding without fully consuming the input string. ** parsing bug fixed Parsing a class whose parent has common parts now works. For example: Foo : Bar | Baz *common* Garply. Bar = "bar". Baz = "baz". Garply = "garply". Previously, Bar.parse("bar garply") would result in a Bar object whose garply field was null. ** added CHANGES file You're looking at it! * History does not report changes previous to 0.4.2. $Id: CHANGES,v 1.43 1997/09/09 03:35:50 dougo Exp $