dexed/cesyms/cesyms.d

271 lines
7.0 KiB
D

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);
}
}