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