From f28a83e8d8c2c58d27540a1c8fb0222ff47ebdba Mon Sep 17 00:00:00 2001
From: Basile Burg <basile.burg@gmx.com>
Date: Wed, 25 Feb 2015 07:55:22 +0100
Subject: [PATCH] new widget tool designed to replace the static explorer and
 based on libdparse

---
 cesyms/cesyms.coedit |  24 ++++
 cesyms/cesyms.d      | 270 +++++++++++++++++++++++++++++++++++++++++++
 cesyms/readme.md     |  13 +++
 3 files changed, 307 insertions(+)
 create mode 100644 cesyms/cesyms.coedit
 create mode 100644 cesyms/cesyms.d
 create mode 100644 cesyms/readme.md

diff --git a/cesyms/cesyms.coedit b/cesyms/cesyms.coedit
new file mode 100644
index 00000000..cb42f221
--- /dev/null
+++ b/cesyms/cesyms.coedit
@@ -0,0 +1,24 @@
+object CurrentProject: TCEProject
+  OptionsCollection = <  
+    item
+      name = 'release'
+      outputOptions.inlining = True
+      outputOptions.boundsCheck = offAlways
+      outputOptions.optimizations = True
+      outputOptions.release = True
+      pathsOptions.outputFilename = '..\lazproj\cesyms'
+      preBuildProcess.options = []
+      preBuildProcess.showWindow = swoNone
+      postBuildProcess.options = []
+      postBuildProcess.showWindow = swoNone
+      runOptions.options = []
+      runOptions.showWindow = swoNone
+    end>
+  Sources.Strings = (
+    'cesyms.d'
+  )
+  ConfigurationIndex = 0
+  LibraryAliases.Strings = (
+    'libdparse'
+  )
+end
diff --git a/cesyms/cesyms.d b/cesyms/cesyms.d
new file mode 100644
index 00000000..63d54085
--- /dev/null
+++ b/cesyms/cesyms.d
@@ -0,0 +1,270 @@
+module cesyms;
+
+import std.stdio, std.path, std.file, std.array, std.string;
+import std.d.lexer, std.d.ast, std.d.parser;
+static import std.conv;
+
+interface I{}
+
+alias Int32 = int;
+//alias long Int64;
+
+enum E 
+{
+    e1,
+    e2,
+    e3,
+}
+
+enum {opt1,opt2}
+
+class A
+{
+    class AA
+    {
+        class AA1{}
+        class AA2{}
+    }
+    
+    class BB
+    {
+        class BB1{}
+        class BB2{}
+    }
+}
+
+enum SymbolType
+{
+    _alias,     // X
+    _class,     // X
+    _enum,      // X
+    _function,  // X
+    _interface, // X
+    _import,    // X
+    _mixin,
+    _struct,    // X
+    _template,  // X
+    _union,     // X
+    _variable   // X
+}
+
+struct Symbol
+{
+    int line;
+    int col;
+    string name;
+    SymbolType type; 
+    Symbol * [] subs;
+    
+    void serialize(ref Appender!string lfmApp)
+    {
+        lfmApp.put("  \r    item\r");
+        
+        lfmApp.put(format("    line = %d\r", line));
+        lfmApp.put(format("    col = %d\r", col));
+        lfmApp.put(format("    name = '%s'\r", name));
+        lfmApp.put(format("    symType = %s\r", type));
+         
+        lfmApp.put("    subs = <");
+        if (subs.length) foreach(Symbol * sub; subs)
+            sub.serialize(lfmApp);
+        lfmApp.put(">\r");
+        lfmApp.put("    end\r");
+    }
+}
+
+void main(string[] args)
+{
+    if (args.length < 2) return;
+    auto fname = args[1];
+    if (!fname.exists) return;
+
+    // load and parse the file
+    auto config = LexerConfig(fname, StringBehavior.source, WhitespaceBehavior.include);
+    auto source = cast(ubyte[]) read(fname, size_t.max);
+    auto scache = StringCache(StringCache.defaultBucketCount);
+    auto ast = parseModule(getTokensForParser(source, config, &scache), fname);
+
+    // visit each root member
+    auto slb = new SymbolListBuilder; 
+    foreach(Declaration decl; ast.declarations)
+    {
+        slb.resetRoot;
+        slb.visit(decl);
+    } 
+    
+    
+    // TODO-cfeature: Outputs the symbol tree in a format handlable by a Coedit widget
+    
+    int level = -1;
+    void print(Symbol * s)
+    {
+        foreach(i; 0 .. level) write(".");
+        level++;
+        write(s.name, '\r');
+        foreach(ss; s.subs)
+            print(ss);
+        
+        level--;
+    }
+    //print(&slb.root);
+    
+    auto str = slb.serialize;
+    
+    //std.file.write(r"C:\too.txt",cast(ubyte[])str);
+    
+    write(str);
+    stdout.flush;
+}
+
+class SymbolListBuilder : ASTVisitor
+{
+    Symbol root;
+    Symbol * parent;
+    size_t count;
+    
+    alias visit = ASTVisitor.visit;
+    
+    this(){resetRoot;}
+    
+    void resetRoot(){parent = &root;}
+    
+    string serialize()
+    {
+        Appender!string lfmApp;
+        lfmApp.reserve(count * 64);
+        
+        lfmApp.put("object TSymbolList\r  symbols = <");
+        foreach(Symbol * sym; root.subs) sym.serialize(lfmApp);
+        lfmApp.put(">\rend\r\n");
+        
+        return lfmApp.data;
+        
+    }
+    
+    /// returns a new symbol if the declarator is based on a Token named "name".
+    Symbol * addDeclaration(DT)(DT adt)
+    {
+        static if 
+        (
+            is(DT == const(AliasInitializer))       ||
+            is(DT == const(ClassDeclaration))       ||
+            is(DT == const(Declarator))             ||
+            is(DT == const(EnumDeclaration))        ||
+            is(DT == const(FunctionDeclaration))    || 
+            is(DT == const(InterfaceDeclaration))   || 
+            is(DT == const(StructDeclaration))      || 
+            is(DT == const(TemplateDeclaration))    ||
+            is(DT == const(UnionDeclaration)) 
+            
+        )
+        {
+            count++;
+            auto result = new Symbol;
+            result.name = adt.name.text.idup;
+            result.line = adt.name.line;
+            result.col  = adt.name.column;             
+            parent.subs ~= result;  
+            return result;
+        }
+        
+        assert(0, "addDeclaration no implemented for " ~ DT.stringof);
+    }
+    
+    /// visitor implementation if the declarator is based on a Token named "name".
+    void namedVisitorImpl(DT, SymbolType st, bool dig = true)(const(DT) dt)
+    {
+        auto newSymbol = addDeclaration(dt);
+        newSymbol.type = st;
+        //
+        static if (dig)
+        {
+            auto previousParent = parent;
+            scope(exit) parent = previousParent;
+            parent = newSymbol;
+            dt.accept(this);
+        }       
+    }
+    
+    final override void visit(const AliasDeclaration decl) 
+    { 
+        // old alias syntax not supported by this.
+        // why is initializers an array ?
+        if (decl.initializers.length == 1)
+            namedVisitorImpl!(AliasInitializer, SymbolType._alias)(decl.initializers[0]);  
+    }
+     
+    final override void visit(const ClassDeclaration decl) 
+    {
+        namedVisitorImpl!(ClassDeclaration, SymbolType._class)(decl);
+    }
+    
+    final override void visit(const EnumDeclaration decl) 
+    {
+        // TODO-ctest: try to see if what dmd outputs as , "enum member" is handled.
+        namedVisitorImpl!(EnumDeclaration, SymbolType._class)(decl);
+    }
+    
+    final override void visit(const FunctionDeclaration decl) 
+    {
+        namedVisitorImpl!(FunctionDeclaration, SymbolType._function)(decl);    
+    }
+    
+    final override void visit(const InterfaceDeclaration decl) 
+    {
+        namedVisitorImpl!(InterfaceDeclaration, SymbolType._interface)(decl);    
+    }
+    
+    final override void visit(const ImportDeclaration decl) 
+    {
+        foreach(const(SingleImport) si; decl.singleImports)
+        { 
+            if (!si.identifierChain.identifiers.length)
+                continue;
+            //
+            string[] modules;
+            foreach(ident; si.identifierChain.identifiers)
+            {
+                modules ~= ident.text;
+                modules ~= ".";
+            }
+            //
+            count++;
+            auto result = new Symbol;
+            result.name = modules[0..$-1].join;
+            result.line = si.identifierChain.identifiers[0].line;
+            result.col  = si.identifierChain.identifiers[0].column; 
+            result.type = SymbolType._import;            
+            parent.subs ~= result;  
+        }   
+    }
+    
+    final override void visit(const MixinDeclaration decl) 
+    {
+        // TODO-cfeature: MixinDeclaration, just display the name of the mixed template.
+        // the template might be implemented in another module so their ùeùbrs cant be displayed.
+    }
+    
+    final override void visit(const StructDeclaration decl) 
+    {
+        namedVisitorImpl!(StructDeclaration, SymbolType._struct)(decl);   
+    }
+    
+    final override void visit(const TemplateDeclaration decl) 
+    {
+        namedVisitorImpl!(TemplateDeclaration, SymbolType._function)(decl);    
+    }
+    
+    final override void visit(const UnionDeclaration decl) 
+    {
+        namedVisitorImpl!(UnionDeclaration, SymbolType._function)(decl);    
+    }
+    
+    final override void visit(const VariableDeclaration decl) 
+    {
+        foreach(elem; decl.declarators)
+            namedVisitorImpl!(Declarator, SymbolType._variable, false)(elem);  
+    }
+}
+
+
diff --git a/cesyms/readme.md b/cesyms/readme.md
new file mode 100644
index 00000000..2e3ba8db
--- /dev/null
+++ b/cesyms/readme.md
@@ -0,0 +1,13 @@
+ceSyms
+======
+
+Tool designed to build a symbol tree for a particular D module.
+It's written in D using Coedit. To build it, [libdparse](https://github.com/Hackerpilot/libdparse)
+must be setup in the [libman](https://github.com/BBasile/Coedit/wiki#library-manager-widget) 
+as described in this [tutorial](https://github.com/BBasile/Coedit/wiki#lets-build-a-static-library).
+
+This tool is mandatory to enable the new _Symbol list widget_. 
+If missing, the old _static explorer widget_ still does the same but it's from far less
+efficient since it actually compiles while only the AST is needed.
+
+The new tool does not take the module correctness in account.
\ No newline at end of file