From 90b4108f4586d850a63f40ce2624527a7a270b97 Mon Sep 17 00:00:00 2001 From: cremet Date: Wed, 30 Jul 2003 08:27:43 +0000 Subject: Added: - collecting of documentation comments. - options for scaladoc - a class to load a documentation module. --- config/list/compiler.lst | 1 + sources/scalac/CompilerCommand.java | 15 +++++ sources/scalac/DocModule.java | 100 +++++++++++++++++++++++++++++ sources/scalac/Global.java | 22 +++++++ sources/scalac/ast/parser/Parser.java | 54 +++++++++++++--- sources/scalac/ast/parser/Scanner.java | 31 ++++++--- sources/scalac/typechecker/Analyzer.java | 7 +- sources/scalac/typechecker/DeSugarize.java | 31 ++++++--- 8 files changed, 233 insertions(+), 28 deletions(-) create mode 100644 sources/scalac/DocModule.java diff --git a/config/list/compiler.lst b/config/list/compiler.lst index 29a15f09a7..75c0fdfcee 100644 --- a/config/list/compiler.lst +++ b/config/list/compiler.lst @@ -11,6 +11,7 @@ Phase.java PhaseDescriptor.java PhaseRepository.java Unit.java +DocModule.java ast/parser/PatternNormalizer.java ast/parser/Parser.java diff --git a/sources/scalac/CompilerCommand.java b/sources/scalac/CompilerCommand.java index 94f04963e7..5b87d72b46 100644 --- a/sources/scalac/CompilerCommand.java +++ b/sources/scalac/CompilerCommand.java @@ -56,6 +56,9 @@ public class CompilerCommand extends CommandParser { public final ChoiceOptionParser printer; public final StringOptionParser printfile; public final PhaseSetOptionParser graph; + public final BooleanOptionParser doc; + public final StringOptionParser docmodule; + public final StringOptionParser docmodulePath; public final PhaseSetOptionParser stop; public final PhaseSetOptionParser log; public final VersionOptionParser version; @@ -170,6 +173,18 @@ public class CompilerCommand extends CommandParser { "graph", "Graph the program after (see below)", phases.phases, PhaseDescriptor.GRAPH), + this.doc = new BooleanOptionParser(this, + "doc", "Generate documentation", + false), + + this.docmodule = new StringOptionParser(this, + "docmodule", "Specify module used by scaladoc", + "class", "scaladoc.StandardDocModule"), + + this.docmodulePath = new StringOptionParser(this, + "docmodulepath", "Specify where to find doc module class files", + "path", ClassPath.CLASS_PATH), + this.stop = new PhaseSetOptionParser(this, "stop", "Stop after first phase in (see below)", phases.phases, PhaseDescriptor.STOP), diff --git a/sources/scalac/DocModule.java b/sources/scalac/DocModule.java new file mode 100644 index 0000000000..cad5c1f1ab --- /dev/null +++ b/sources/scalac/DocModule.java @@ -0,0 +1,100 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +package scalac; + +import scalac.Global; +import scalac.util.Debug; +import scalac.util.Reporter; +import ch.epfl.lamp.util.Position; +import java.net.URL; +import java.net.URLClassLoader; +import java.io.File; +import java.util.StringTokenizer; +import java.util.List; +import java.util.LinkedList; +import java.lang.reflect.Method; + +/** + * This class is used to dynamically load a documentation module. + */ +public class DocModule { + + /** Name of the method to be called (convention). + */ + protected static final String METHOD_NAME = "apply"; + + /** Complete the given path with standard class paths. + */ + public static String completePath(String path) { + String completePath = path; + String path1 = System.getProperty("env.class.path"); + String path2 = System.getProperty("java.class.path"); + if (path1 != null) + completePath += File.pathSeparator + path1; + if (path2 != null) + completePath += File.pathSeparator + path2; + return completePath; + } + + /** Get class loader from a given path. + */ + public static ClassLoader getClassLoader(String path) { + List urlList = new LinkedList(); + StringTokenizer st = new StringTokenizer(path, File.pathSeparator); + try { + while (st.hasMoreTokens()) + urlList.add(new File(st.nextToken()).toURI().toURL()); + } catch(java.net.MalformedURLException e) { + throw Debug.abort(e); + } + URL[] urls = (URL[]) urlList.toArray(new URL[urlList.size()]); + return new URLClassLoader(urls); + } + + /** Get a class from its name. + */ + public static Class getClass(String className, ClassLoader classLoader) { + Class cls = null; + try { + cls = classLoader.loadClass(className); + } catch (ClassNotFoundException exc) { + throw Debug.abort("Class not found", className); + } + return cls; + } + + /** Get the method + */ + public static Method getMethod(String methodName, Class cls) { + Method meth = null; + try { + meth = cls.getMethod(methodName, + new Class[] { Class.forName("scalac.Global") }); + } catch (Exception e) { + throw Debug.abort(e); + } + return meth; + } + + public static void apply(Global global) { + // complete path + String path = completePath(global.docmodulePath); + // class loader + ClassLoader classLoader = getClassLoader(global.docmodulePath); + // module class + Class cls = getClass(global.docmodule, classLoader); + // method + Method meth = getMethod(METHOD_NAME, cls); + // call method + Thread.currentThread().setContextClassLoader(classLoader); + try { + meth.invoke(null, new Object[] { global }); + } catch(Exception e) { + throw Debug.abort(e); + } + } +} diff --git a/sources/scalac/Global.java b/sources/scalac/Global.java index ad0eed1eb7..b5ef83b8ab 100644 --- a/sources/scalac/Global.java +++ b/sources/scalac/Global.java @@ -86,6 +86,20 @@ public class Global { public OutputStream printStream; public final TreePrinter debugPrinter; + /** documentation comments of trees + */ + public final Map/**/ mapTreeComment = new HashMap(); + + /** documentation comments of symbols + */ + public final Map/**/ mapSymbolComment = new HashMap(); + + /** scaladoc option (with docmodule and docmodulepath) + */ + public final boolean doc; + public final String docmodule; + public final String docmodulePath; + /** The set of currenttly compiled top-level symbols */ public HashMap/**/ compiledNow = new HashMap(); @@ -174,6 +188,9 @@ public class Global { else this.printer = new HTMLTreePrinter(printStream); this.debugPrinter = new TextTreePrinter(System.err, true); + this.doc = args.doc.value; + this.docmodule = args.docmodule.value; + this.docmodulePath = args.docmodulePath.value; this.freshNameCreator = new FreshNameCreator(); this.make = new DefaultTreeFactory(); this.currentPhase = PhaseDescriptor.INITIAL; @@ -288,6 +305,11 @@ public class Global { } if (currentPhase == PHASE.PARSER) fix1(); if (currentPhase == PHASE.ANALYZER) fix2(); + if (currentPhase == PHASE.ANALYZER && doc) { + DocModule.apply(this); + operation("stopped after phase " + currentPhase.name()); + break; + } } if (reporter.errors() != 0) { imports.clear(); diff --git a/sources/scalac/ast/parser/Parser.java b/sources/scalac/ast/parser/Parser.java index d5b3522367..00670ab8fc 100644 --- a/sources/scalac/ast/parser/Parser.java +++ b/sources/scalac/ast/parser/Parser.java @@ -40,6 +40,7 @@ public class Parser implements Tokens { s = new Scanner(unit); make = unit.global.make; pN = new PatternNormalizer( unit ); + mapTreeComment = unit.global.mapTreeComment; } /** this is the general parse method @@ -161,6 +162,38 @@ public class Parser implements Tokens { } } +/////// COMMENT COLLECTION /////////////////////////////////////////////////// + + /** keep the comments associated with a given tree + */ + protected Map mapTreeComment; + + /** stack of comments + */ + protected final Stack commentStack = new Stack(); + + /** positive if we are inside a block + */ + protected int local = 0; + + /** push last encountered comment and reset the buffer + */ + protected void pushComment() { + if (local == 0) { + commentStack.push(s.docBuffer == null ? null : s.docBuffer.toString()); + s.docBuffer = null; + } + } + + /** pop a comment from the stack and associate it with the given tree + */ + protected Tree popComment(Tree tree) { + if (local == 0) + if (!commentStack.empty()) + mapTreeComment.put(tree, (String) commentStack.pop()); + return tree; + } + /////// TREE CONSTRUCTION //////////////////////////////////////////////////// /** Name supply @@ -995,6 +1028,7 @@ public class Parser implements Tokens { * | `{' Block `}' */ Tree blockExpr() { + local++; Tree res; int pos = accept(LBRACE); if (s.token == CASE) { @@ -1008,6 +1042,7 @@ public class Parser implements Tokens { res = block(pos); } accept(RBRACE); + local--; return res; } @@ -1265,6 +1300,7 @@ public class Parser implements Tokens { * | abstract */ int modifiers() { + pushComment(); int mods = 0; while (true) { int mod; @@ -1551,25 +1587,25 @@ public class Parser implements Tokens { case VAL: do { s.nextToken(); - ts.append(patDefOrDcl(mods)); + ts.append(popComment(patDefOrDcl(mods))); } while (s.token == COMMA); return ts.toArray(); case VAR: do { s.nextToken(); - ts.append(varDefOrDcl(mods)); + ts.append(popComment(varDefOrDcl(mods))); } while (s.token == COMMA); return ts.toArray(); case DEF: do { s.nextToken(); - ts.append(funDefOrDcl(mods)); + ts.append(popComment(funDefOrDcl(mods))); } while (s.token == COMMA); return ts.toArray(); case TYPE: do { s.nextToken(); - ts.append(typeDefOrDcl(mods)); + ts.append(popComment(typeDefOrDcl(mods))); } while (s.token == COMMA); return ts.toArray(); default: @@ -1725,11 +1761,11 @@ public class Parser implements Tokens { ValDef[][] params = paramClauseOpt(); TreeList result = new TreeList(); result.append( - make.ClassDef(pos, mods, clazzname, tparams, params, - simpleTypedOpt(), classTemplate())); + popComment(make.ClassDef(pos, mods, clazzname, tparams, params, + simpleTypedOpt(), classTemplate()))); while (s.token == CONSTR) { s.nextToken(); - result.append(constrDef(mods, clazzname, tparams)); + result.append(popComment(constrDef(mods, clazzname, tparams))); } return result.toArray(); } @@ -1737,8 +1773,8 @@ public class Parser implements Tokens { /** ObjectDef ::= Id [`:' SimpleType] ClassTemplate */ Tree objectDef(int mods) { - return make.ModuleDef( - s.pos, mods, ident(), simpleTypedOpt(), classTemplate()); + return popComment(make.ModuleDef( + s.pos, mods, ident(), simpleTypedOpt(), classTemplate())); } /** ClassTemplate ::= [`extends' Constr] {`with' Constr} [TemplateBody] diff --git a/sources/scalac/ast/parser/Scanner.java b/sources/scalac/ast/parser/Scanner.java index bc153c869f..c747fc1bcc 100644 --- a/sources/scalac/ast/parser/Scanner.java +++ b/sources/scalac/ast/parser/Scanner.java @@ -23,6 +23,16 @@ import scalac.util.Name; */ public class Scanner extends TokenData { + /** buffer for the documentation comment + */ + protected StringBuffer docBuffer = null; + + /** add the given character to the documentation buffer + */ + protected void addCharToDoc(byte ch) { + if (docBuffer != null) docBuffer.append((char) ch); + } + /** layout & character constants */ public int tabinc = 8; @@ -342,38 +352,43 @@ public class Scanner extends TokenData { } while ((ch != CR) && (ch != LF) && (ch != SU)); return true; } else if (ch == '*') { + docBuffer = null; int openComments = 1; + nextch(); + if (ch == '*') { + docBuffer = new StringBuffer("/**"); + } while (openComments > 0) { do { do { if (ch == CR) { cline++; ccol = 0; - nextch(); + nextch(); addCharToDoc(ch); if (ch == LF) { ccol = 0; - nextch(); + nextch(); addCharToDoc(ch); } } else if (ch == LF) { cline++; ccol = 0; - nextch(); + nextch(); addCharToDoc(ch); } else if (ch == '\t') { ccol = ((ccol - 1) / tabinc * tabinc) + tabinc; - nextch(); + nextch(); addCharToDoc(ch); } else if (ch == '/') { - nextch(); + nextch(); addCharToDoc(ch); if (ch == '*') { - nextch(); + nextch(); addCharToDoc(ch); openComments++; } } else { - nextch(); + nextch(); addCharToDoc(ch); } } while ((ch != '*') && (ch != SU)); while (ch == '*') { - nextch(); + nextch(); addCharToDoc(ch); } } while (ch != '/' && ch != SU); if (ch == '/') { diff --git a/sources/scalac/typechecker/Analyzer.java b/sources/scalac/typechecker/Analyzer.java index bcb7dcb7b7..79b1a0900e 100644 --- a/sources/scalac/typechecker/Analyzer.java +++ b/sources/scalac/typechecker/Analyzer.java @@ -731,7 +731,6 @@ public class Analyzer extends Transformer implements Modifiers, Kinds { case ClassDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, _, Tree.Template templ): ClassSymbol clazz = new ClassSymbol(tree.pos, name, owner, mods); if (clazz.isLocalClass()) unit.mangler.setMangledName(clazz); - enterSym(tree, clazz.constructor()); if ((mods & CASE) != 0) { /* todo: remove @@ -793,6 +792,12 @@ public class Analyzer extends Transformer implements Modifiers, Kinds { sym.flags |= FINAL; sym = enterInScope(sym); tree.setSymbol(sym); + + // set the comment associated with a symbol + String comment = (String) global.mapTreeComment.get(tree); + if (comment != null) + global.mapSymbolComment.put(sym, comment); + return sym; } diff --git a/sources/scalac/typechecker/DeSugarize.java b/sources/scalac/typechecker/DeSugarize.java index b8539a9fa4..e5634390e4 100644 --- a/sources/scalac/typechecker/DeSugarize.java +++ b/sources/scalac/typechecker/DeSugarize.java @@ -394,6 +394,17 @@ public class DeSugarize implements Kinds, Modifiers { } } + /** make a set of trees share the same documentation comment as a + * given tree (used for pattern and val definitions) + */ + Tree[] shareComment(Tree[] trees, Tree tree) { + String comment = (String) global.mapTreeComment.get(tree); + if (comment != null) + for(int i = 0; i < trees.length; i++) + global.mapTreeComment.put(trees[i], comment); + return trees; + } + /** expand pattern definitions and variable definitions in templates. */ public Tree[] Statements(Tree[] stats, boolean isLocal) { @@ -458,13 +469,13 @@ public class DeSugarize implements Kinds, Modifiers { case PatDef(int mods, Ident(Name name), Tree rhs): // val x = e ==> val x = e - return new Tree[]{ - make.ValDef(tree.pos, mods, name, Tree.Empty, rhs)}; + return shareComment(new Tree[]{ + make.ValDef(tree.pos, mods, name, Tree.Empty, rhs)}, tree); case PatDef(int mods, Typed(Ident(Name name), Tree type), Tree rhs): // val x: T = e ==> val x: T = e - return new Tree[]{ - make.ValDef(tree.pos, mods, name, type, rhs)}; + return shareComment(new Tree[]{ + make.ValDef(tree.pos, mods, name, type, rhs)}, tree); case PatDef(int mods, Tree pat, Tree rhs): int pos = tree.pos; @@ -494,7 +505,7 @@ public class DeSugarize implements Kinds, Modifiers { // val x_1 = e.match (case p => x_1) Tree valdef = make.ValDef(pos, mods, vars[0], Tree.Empty, match); print(pat, "patdef", valdef); - return new Tree[]{valdef}; + return shareComment(new Tree[]{valdef}, tree); } else { // t$ Name var = getvar(); @@ -510,7 +521,7 @@ public class DeSugarize implements Kinds, Modifiers { make.Select(pos, make.Ident(pos, var), tupleSelectorName(i + 1))); } print(pat, "patdef", new Block(res));//debug - return res; + return shareComment(res, tree); } default: throw new ApplicationError("pattern definition expected", tree); @@ -533,8 +544,8 @@ public class DeSugarize implements Kinds, Modifiers { ((mods & DEFERRED) != 0) ? Tree.Empty : make.Ident(tree.pos, valname)); if ((mods1 & MUTABLE) == 0) { - if ((mods1 & DEFERRED) != 0) return new Tree[]{getter}; - else return new Tree[]{valdef1, getter}; + if ((mods1 & DEFERRED) != 0) return shareComment(new Tree[]{getter}, tree); + else return shareComment(new Tree[]{valdef1, getter}, tree); } else { Tree setter = make.DefDef( tree.pos, mods1, setterName(name), @@ -548,8 +559,8 @@ public class DeSugarize implements Kinds, Modifiers { tree.pos, make.Ident(tree.pos, valname), make.Ident(tree.pos, parameterName(0)))); - if ((mods1 & DEFERRED) != 0) return new Tree[]{getter, setter}; - else return new Tree[]{valdef1, getter, setter}; + if ((mods1 & DEFERRED) != 0) return shareComment(new Tree[]{getter, setter}, tree); + else return shareComment(new Tree[]{valdef1, getter, setter}, tree); } default: throw new ApplicationError(); -- cgit v1.2.3