changeset 455:f92505ad18ab

Simple configuration reader, small modifications.
author Jari-Matti M?kel? <jmjm@iki.fi>
date Mon, 29 Oct 2007 21:27:02 +0200
parents dbdc9fa5d479
children de2675bc9afa
files trunk/src/docgen/config/configurator.d trunk/src/docgen/config/default.cfg trunk/src/docgen/config/reader.d trunk/src/docgen/docgen.d trunk/src/docgen/document/latexwriter.d trunk/src/docgen/misc/misc.d trunk/src/docgen/tests/common.d trunk/src/docgen/tests/doctemplate.d
diffstat 8 files changed, 418 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/docgen/config/configurator.d	Mon Oct 29 21:27:02 2007 +0200
@@ -0,0 +1,148 @@
+module docgen.config.configurator;
+
+import docgen.config.reader;
+import docgen.misc.misc;
+
+import Integer = tango.text.convert.Integer;
+import tango.io.stream.FileStream;
+
+interface Configurator {
+  void mergeConfiguration(char[] cfgFile);
+  
+  DocGeneratorOptions *getConfiguration();
+}
+
+// ugly piece of crap begins.
+
+char[] _wrong(char[] key) {
+  return `if (val.length != 1) throw new Exception(
+    "Wrong number of arguments for `~key~`");`;
+}
+
+char[] _switch(char[] stuff) {
+  return "switch(key) {" ~ stuff ~ "}";
+}
+
+char[] _parseI(char[] key) {
+  return `case "` ~ key ~ `":` ~ _wrong(key) ~ key ~
+    "= Integer.parse(val[0]); continue;";
+}
+
+char[] _parseS(char[] key) {
+  return `case "` ~ key ~ `":` ~ _wrong(key) ~ key ~
+    "= val[0]; continue;";
+}
+
+char[] _parseB(char[] key) {
+  return `case "` ~ key ~ `":` ~ _wrong(key) ~ key ~
+    `= val[0] == "true" ? true : val[0] == "false" ? false : err(); continue;`;
+}
+
+char[] _parseList(char[] key) {
+  return `case "` ~ key ~ `":foreach(v; val) ` ~
+    key ~ `~= v; continue;`;
+}
+
+template _parseEnum_(bool list, char[] key, V...) {
+  static if (V.length>1)
+    const char[] _parseEnum_ =
+      `case "` ~ V[0] ~ `":` ~ key ~ (list ? "~" : "") ~ `=` ~ V[1] ~ `; continue;` \n ~
+      _parseEnum_!(list, key, V[2..$]);
+  else
+    const char[] _parseEnum_ = "";
+}
+
+template _parseEnum(char[] key, V...) {
+  const char[] _parseEnum = `case "` ~ key ~
+    `":` ~ _wrong(key) ~ `switch(val[0]) {` ~
+    _parseEnum_!(false, key, V) ~
+      `default: err(); }`;
+}
+
+template _parseEnumList(char[] key, V...) {
+  const char[] _parseEnumList = `case "` ~ key ~
+    `":` ~ _wrong(key) ~ `switch(val[0]) {` ~
+    _parseEnum_!(true, key, V) ~
+      `default: err(); }`;
+}
+
+class DefaultConfigurator : Configurator {
+  DocGeneratorOptions options;
+
+  const defaultProfileLocation = "docgen/config/default.cfg";
+
+  this() {
+    mergeConfiguration(defaultProfileLocation);
+  }
+
+  this(char[] cfgFile) {
+    this();
+    mergeConfiguration(cfgFile);
+  }
+
+  void mergeConfiguration(char[] cfgFile) {
+    
+    auto inputStream = new FileInput(cfgFile);
+    auto content = new char[inputStream.length];
+    auto bytesRead = inputStream.read (content);
+    
+    assert(bytesRead == inputStream.length, "Error reading configuration file");
+
+    auto tokens = lex(content);
+    auto configuration = parse(tokens);
+
+    foreach(key, val; configuration) {
+      bool err() {
+        throw new Exception(
+          "Configurator: Invalid key-val pair " ~ key ~
+          "=" ~ (val.length ? val[0] : "null"));
+      }
+
+      mixin(_switch(
+        _parseEnum!("options.graph.imageFormat",
+          "PDF", "ImageFormat.PDF",
+          "SVG", "ImageFormat.SVG",
+          "PNG", "ImageFormat.PNG",
+          "GIF", "ImageFormat.GIF"
+        ) ~
+        _parseI("options.graph.depth") ~
+        _parseS("options.graph.nodeColor") ~
+        _parseS("options.graph.cyclicNodeColor") ~
+        _parseS("options.graph.unlocatableNodeColor") ~
+        _parseS("options.graph.clusterColor") ~
+        _parseB("options.graph.includeUnlocatableModules") ~
+        _parseB("options.graph.highlightCyclicEdges") ~
+        _parseB("options.graph.highlightCyclicVertices") ~
+        _parseB("options.graph.groupByPackageNames") ~
+        _parseB("options.graph.groupByFullPackageName") ~
+        _parseB("options.listing.literateStyle") ~
+        _parseB("options.listing.enableListings") ~
+        _parseS("options.templates.title") ~
+        _parseS("options.templates.versionString") ~
+        _parseS("options.templates.copyright") ~
+        _parseS("options.templates.paperSize") ~
+        _parseB("options.templates.shortFileNames") ~
+        _parseS("options.templates.templateStyle") ~
+        _parseList("options.parser.importPaths") ~
+        _parseList("options.parser.rootPaths") ~
+        _parseList("options.parser.strRegexps") ~
+        _parseEnum!("options.parser.commentFormat",
+            "Doxygen", "CommentFormat.Doxygen",
+            "Ddoc", "CommentFormat.Ddoc"
+        ) ~ 
+        _parseEnumList!("options.outputFormats",
+            "LaTeX", "DocFormat.LaTeX",
+            "HTML", "DocFormat.HTML",
+            "XML", "DocFormat.XML",
+            "PlainText", "DocFormat.PlainText"
+        ) ~
+        _parseS("options.outputDir") ~
+        `default: throw new Exception("Illegal configuration key " ~ key);`
+      ));
+    }
+  }
+
+  DocGeneratorOptions *getConfiguration() {
+    return &options;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/docgen/config/default.cfg	Mon Oct 29 21:27:02 2007 +0200
@@ -0,0 +1,35 @@
+(options
+  (graph
+    (imageFormat PDF)
+    (depth -1)
+    (nodeColor tomato)
+    (cyclicNodeColor red)
+    (unlocatableNodeColor gray)
+    (clusterColor blue)
+    (includeUnlocatableModules false)
+    (highlightCyclicEdges true)
+    (highlightCyclicVertices true)
+    (groupByPackageNames true)
+    (groupByFullPackageName false)
+  )
+  (listing
+    (literateStyle true)
+    (enableListings true)
+  )
+  (templates
+    (title "Test project")
+    (versionString 1.0)
+    (copyright "(C) Me!")
+    (paperSize a4paper)
+    (shortFileNames false)
+    (templateStyle default)
+  )
+  (parser
+    (importPaths)
+    (rootPaths)
+    (strRegexps)
+    (commentFormat Doxygen)
+  )
+  (outputFormats LaTeX)
+  (outputDir tmp/)
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/src/docgen/config/reader.d	Mon Oct 29 21:27:02 2007 +0200
@@ -0,0 +1,130 @@
+module docgen.config.reader;
+
+debug import tango.io.Stdout;
+
+char[][] lex(char[] input) {
+  char[][] tokens;
+
+  uint state = 0, level = 0;
+  size_t sidx = 0;
+
+  void err(size_t i, int type = 0) {
+    auto b = input[i<20 ? 0 : i-20..i];
+    auto e = input[i+1..i+21>$ ? $ : i+21];
+
+    throw new Exception("Lex: " ~ 
+      (type == 0 ? "Illegal character" :
+      type == 1 ? "Wrong number of parenthesis" :
+      "Unexpected end of input") ~
+      ": " ~ b ~ "  >>>" ~ input[i] ~ "<<<  " ~ e ~ "."
+    );
+  }
+  void begin(size_t i) { sidx = i; }
+  void end(size_t i) { tokens ~= input[sidx..i]; }
+
+  foreach(size_t i, c; input) {
+    if (sidx > i) continue;
+    switch(c) { // states space, token, textEnd
+      case '"':
+        switch(state) {
+          case 0:
+            char[] buf;
+            bool escape;
+            char d;
+            sidx = i;
+            while(!((d = input[++sidx]) == '"' && !escape) && sidx<input.length)
+              if (escape) {
+                if (d != '"' && d != '\\') buf ~= '\\';
+                buf ~= d;
+                escape = false;
+              } else if (d == '\\')
+                escape = true;
+              else
+                buf ~= d;
+
+            sidx++;
+            tokens ~= buf;
+            state = 2;
+            continue;
+          default: err(i);
+        }
+      case '\\':
+        switch(state) {
+          case 0: begin(i); state = 1; continue;
+          case 1: continue;
+          case 2: err(i);
+        }
+      case ' ':
+      case '\t':
+      case '\n':
+      case '(':
+      case ')':
+        switch(state) {
+          case 1: end(i);
+          case 0:
+          case 2:
+            switch(c) {
+              case '(': tokens ~= "("; level++; state = 0; break;
+              case ')': tokens ~= ")"; if (!level--) err(i,1);
+              default: state = 0;
+            }
+        }
+        break;
+      default:
+        switch(state) {
+          case 0: begin(i);
+          case 1: state = 1; continue;
+          case 2: err(i);
+        }
+     }
+  }
+
+  if (state == 3 || level != 0) err(input.length-1,2);
+  if (state > 0) end(input.length);
+
+  debug {
+    foreach(i, tok; tokens)
+      Stdout.format("{}{}", tok, (i != tokens.length-1 ? " " : ""));
+    Stdout.newline;
+  }
+
+  return tokens;
+}
+
+char[][][char[]] parse(char[][] tokens) {
+  char[][][char[]] values;
+  size_t i = 1;
+
+  void err(size_t j) {
+    auto b = tokens[j < 5 ? 0 : j-5..j];
+    auto e = tokens[j+1..j+6>$ ? $ : j+6];
+    char[] tmp;
+    foreach(t; b) tmp ~= t ~ " ";
+    tmp ~= ">>>" ~ tokens[j] ~ "<<< ";
+    foreach(t; e) tmp ~= t ~ " ";
+
+    throw new Exception(
+      "Parse: Illegal token: " ~ tmp ~ "."
+    );
+  }
+
+  if (tokens[0] != "(") err(0);
+
+  void doParse(char[] prefix = null) {
+    if (tokens[i] == "(" ||
+        tokens[i] == ")") err(i);
+    if (prefix) prefix ~= ".";
+    auto v = prefix ~ tokens[i++];
+    //values[v] = null;
+    while (tokens[i] != ")")
+      if (tokens[i++] == "(")
+        doParse(v);
+      else
+        values[v] ~= tokens[i-1];
+    i++;
+  }
+
+  doParse();
+
+  return values;
+}
--- a/trunk/src/docgen/docgen.d	Fri Oct 26 01:04:09 2007 +0200
+++ b/trunk/src/docgen/docgen.d	Mon Oct 29 21:27:02 2007 +0200
@@ -9,87 +9,92 @@
 import docgen.graphutils.writers;
 import docgen.misc.misc;
 import docgen.misc.parser;
+import docgen.config.configurator;
 import tango.core.Array;
 import tango.io.stream.FileStream;
 import tango.text.Ascii;
 import tango.text.Util : replace;
+import tango.io.FilePath;
 debug import tango.io.Stdout;
 
-abstract class DefaultDocGenerator : DocGenerator {
-  DocGeneratorOptions m_options;
-  DocumentWriter docWriter;
-  GraphWriterFactory graphFactory;
-  
-  Module[] modules;
-  Edge[] edges;
-  Vertex[char[]] vertices;
+template DefaultDocGenerator(char[] genDir) {
+  abstract class DefaultDocGenerator : DocGenerator {
+    DocGeneratorOptions m_options;
+    DocumentWriter docWriter;
+    GraphWriterFactory graphFactory;
+    
+    Module[] modules;
+    Edge[] edges;
+    Vertex[char[]] vertices;
 
-  this(DocGeneratorOptions options) {
-    m_options = options;
-    parseSources();
-    graphFactory = new DefaultGraphWriterFactory(this);
-  }
+    this(DocGeneratorOptions options) {
+      m_options = options;
+      graphFactory = new DefaultGraphWriterFactory(this);
+
+      // create output dir
+      (new FilePath(options.outputDir ~ "/" ~ genDir)).create();
+    }
 
-  // TODO: constructor for situations where parsing has happened elsewhere
+    // TODO: constructor for situations where parsing has happened elsewhere
 
-  char[] outPath(char[] file) {
-    return options.outputDir ~ "/" ~ file;
-  }
+    char[] outPath(char[] file) {
+      return options.outputDir ~ "/" ~ genDir ~ "/" ~ file;
+    }
 
-  void parseSources() {
-    int id = 1;
+    void parseSources() {
+      int id = 1;
 
-    Parser.loadModules(
-      options.parser.rootPaths,
-      options.parser.importPaths,
-      options.parser.strRegexps,
-      options.graph.includeUnlocatableModules,
-      options.graph.depth,
-      (char[] fqn, char[] path, Module m) {
-        if (m is null) {
-          if (fqn in vertices) {
-            debug Stdout.format("{} already set.\n", fqn);
-            return;
+      Parser.loadModules(
+        options.parser.rootPaths,
+        options.parser.importPaths,
+        options.parser.strRegexps,
+        options.graph.includeUnlocatableModules,
+        options.graph.depth,
+        (char[] fqn, char[] path, Module m) {
+          if (m is null) {
+            if (fqn in vertices) {
+              debug Stdout.format("{} already set.\n", fqn);
+              return;
 
-          }
-          auto vertex = new Vertex(fqn, path, id++);
-          vertex.type = VertexType.UnlocatableModule;
-          vertices[fqn] = vertex;
-          debug Stdout.format("Setting {} = {}.\n", fqn, path);
+            }
+            auto vertex = new Vertex(fqn, path, id++);
+            vertex.type = VertexType.UnlocatableModule;
+            vertices[fqn] = vertex;
+            debug Stdout.format("Setting {} = {}.\n", fqn, path);
 
-        } else {
-          vertices[m.moduleFQN] = new Vertex(m.moduleFQN, m.filePath, id++);
-          debug Stdout.format("Setting {} = {}.\n", m.moduleFQN, m.filePath);
-        }
-      },
-      (Module imported, Module importer) {
-        debug Stdout.format("Connecting {} - {}.\n", imported.moduleFQN, importer.moduleFQN);
-        edges ~= vertices[imported.moduleFQN].addChild(vertices[importer.moduleFQN]);
-      },
-      modules
-    );
-  }
+          } else {
+            vertices[m.moduleFQN] = new Vertex(m.moduleFQN, m.filePath, id++);
+            debug Stdout.format("Setting {} = {}.\n", m.moduleFQN, m.filePath);
+          }
+        },
+        (Module imported, Module importer) {
+          debug Stdout.format("Connecting {} - {}.\n", imported.moduleFQN, importer.moduleFQN);
+          edges ~= vertices[imported.moduleFQN].addChild(vertices[importer.moduleFQN]);
+        },
+        modules
+      );
+    }
 
-  void createDepGraph(char[] depGraphFile) {
-    auto imgFile = new FileOutput(outPath(depGraphFile));
+    void createDepGraph(char[] depGraphFile) {
+      auto imgFile = new FileOutput(outPath(depGraphFile));
 
-    auto writer = graphFactory.createGraphWriter( docWriter, GraphFormat.Dot );
+      auto writer = graphFactory.createGraphWriter( docWriter, GraphFormat.Dot );
 
-    writer.generateDepGraph(vertices.values, edges, imgFile);
+      writer.generateDepGraph(vertices.values, edges, imgFile);
 
-    imgFile.close();
-  }
+      imgFile.close();
+    }
 
-  public DocGeneratorOptions *options() {
-    return &m_options;
-  }
+    public DocGeneratorOptions *options() {
+      return &m_options;
+    }
 }
-
+}
 
 /**
  * Main routine for LaTeX doc generation.
  */
-class LaTeXDocGenerator : DefaultDocGenerator {
+class LaTeXDocGenerator : DefaultDocGenerator!("latex") {
   this(DocGeneratorOptions options) {
     super(options);
   }
@@ -190,13 +195,15 @@
     auto docFileName = "document.tex";
     auto depGraphTexFile = "dependencies.tex";
     auto depGraphFile = "depgraph.dot";
-    auto listingsFile = "files.tex";
+    auto listingFile = "files.tex";
     auto modulesFile = "modules.tex";
 
+    parseSources();
+
     generateDoc(docFileName);
 
-    if (options.listings.enableListings)
-      generateListings(listingsFile);
+    if (options.listing.enableListings)
+      generateListings(listingFile);
 
     generateModules(modulesFile);
 
@@ -208,40 +215,14 @@
 }
 
 void main(char[][] args) {
-  DocGeneratorOptions options;
-
-  options.graph.imageFormat = ImageFormat.PDF;
-  options.graph.depth = -1;
-  options.graph.nodeColor = "tomato";
-  options.graph.cyclicNodeColor = "red";
-  options.graph.unlocatableNodeColor = "gray";
-  options.graph.clusterColor = "blue";
-  options.graph.includeUnlocatableModules = false;
-  options.graph.highlightCyclicEdges = true;
-  options.graph.highlightCyclicVertices = true;
-  options.graph.groupByPackageNames = true;
-  options.graph.groupByFullPackageName = false;
-  
-  options.listings.literateStyle = true;
-  options.listings.enableListings = true;
+  Configurator config = new DefaultConfigurator();
 
-  options.templates.title = "Test project";
-  options.templates.versionString = "1.0";
-  options.templates.copyright = "(C) Me!";
-  options.templates.paperSize = "a4paper";
-  options.templates.shortFileNames = false;
-  options.templates.templateStyle = "default";
-
-  options.parser.importPaths = [ args[2] ];
+  auto options = config.getConfiguration();
   options.parser.rootPaths = [ args[1] ];
-  options.parser.strRegexps = null;
+  options.parser.importPaths = [ args[2] ];
+  options.outputDir = args[3];
 
-  options.outputFormats = [ DocFormat.LaTeX ];
-  options.parser.commentFormat = CommentFormat.Doxygen;
-  options.outputDir = args[3];
-  
-
-  auto generator = new LaTeXDocGenerator(options);
+  auto generator = new LaTeXDocGenerator(*options);
 
   generator.generate();
 }
--- a/trunk/src/docgen/document/latexwriter.d	Fri Oct 26 01:04:09 2007 +0200
+++ b/trunk/src/docgen/document/latexwriter.d	Mon Oct 29 21:27:02 2007 +0200
@@ -27,7 +27,7 @@
       factory.options.templates.versionString,
       docgen_version,
       timeNow(),
-      factory.options.listings.literateStyle ? "" : "%"
+      factory.options.listing.literateStyle ? "" : "%"
     );
   }
 }
--- a/trunk/src/docgen/misc/misc.d	Fri Oct 26 01:04:09 2007 +0200
+++ b/trunk/src/docgen/misc/misc.d	Mon Oct 29 21:27:02 2007 +0200
@@ -45,39 +45,50 @@
 }
 
 struct GraphOptions {
+  /// image format to use for graphs
   ImageFormat imageFormat;
+  /// maximum depth of dependencies in graphs
   uint depth;
-  char[] nodeColor = "tomato";
-  char[] cyclicNodeColor = "red";
-  char[] unlocatableNodeColor = "gray";
-  char[] clusterColor = "blue";
+  /// color of normal modules
+  char[] nodeColor;
+  /// color of the modules in cyclic dep relation
+  char[] cyclicNodeColor;
+  /// unlocatable module color
+  char[] unlocatableNodeColor;
+  /// package color
+  char[] clusterColor;
+  /// include unlocatable modules to the dep graph
   bool includeUnlocatableModules;
+  /// highlight imports in cyclic dep relation
   bool highlightCyclicEdges;
+  /// highlight modules in cyclic dep relation
   bool highlightCyclicVertices;
+  /// group modules by package names in dep graph
   bool groupByPackageNames;
+  /// group modules hierarchically or by full package name
   bool groupByFullPackageName;
 }
 
 struct ListingOptions {
   /// use literate programming symbols [LaTeX]
-  bool literateStyle = true;
+  bool literateStyle;
   /// enable source code listings
   bool enableListings;
 }
 
 struct TemplateOptions {
   /// project title
-  char[] title = "Test project";
+  char[] title;
   /// project version
-  char[] versionString = "1.0";
+  char[] versionString;
   /// copyright notice
   char[] copyright;
   /// paper size [LaTeX]
-  char[] paperSize = "a4paper";
+  char[] paperSize;
   /// use short file names [HTML]
   bool shortFileNames;
   /// page template style to use, customizable via docgen/templates
-  char[] templateStyle = "default";
+  char[] templateStyle;
 }
 
 struct ParserOptions {
@@ -95,10 +106,11 @@
   /// location for the generated output
   char[] outputDir;
 
+  /// list of document formats to be generated
   DocFormat[] outputFormats;
   
   GraphOptions graph;
-  ListingOptions listings;
+  ListingOptions listing;
   TemplateOptions templates;
   ParserOptions parser;
 }
--- a/trunk/src/docgen/tests/common.d	Fri Oct 26 01:04:09 2007 +0200
+++ b/trunk/src/docgen/tests/common.d	Mon Oct 29 21:27:02 2007 +0200
@@ -5,15 +5,20 @@
 module docgen.tests.common;
 
 import docgen.misc.misc;
+import docgen.config.configurator;
 
 class TestDocGenerator : DocGenerator {
-  DocGeneratorOptions m_options;
-  
+  Configurator config;
+
+  this() {
+    config = new DefaultConfigurator();
+  }
+
   public void generate() {
 
   }
   
   public DocGeneratorOptions *options() {
-    return &m_options;
+    return config.getConfiguration();
   }
-}
\ No newline at end of file
+}
--- a/trunk/src/docgen/tests/doctemplate.d	Fri Oct 26 01:04:09 2007 +0200
+++ b/trunk/src/docgen/tests/doctemplate.d	Mon Oct 29 21:27:02 2007 +0200
@@ -12,7 +12,6 @@
 //@unittest
 void doctemplate1() {
   auto gen = new TestDocGenerator;
-  gen.options.outputFormats = [ DocFormat.LaTeX ];
   auto fname = "doctemplate.tex";
   
   auto gwf = new DefaultDocumentWriterFactory(gen);