I just tried to use the DMD frontend as a Dub package in my own project to parse and analyze D files and it ... just worked. The dub.json for dmd is fairly small and doesn't require any changes to the code. This might be common knowledge, but I was completely unprepared for this :)
Please note: This is "only" the dmd frontend (lexer, parser, semantic passes, CTFE). Code generation will be more complicated.
The following dub pre-generation hooks were necessary:
- run src/idgen.d to generate src/id.d
- create verstr.h and SYSCONFDIR.imp
- create and link source/ddmd/ to src/
The last point makes ddmd modules reside in its correct package. I'm using a symbolic link for this, which is the only reason this approach is currently limited to Linux. In the long run, I think the cleanest way would be to place all ddmd files in src/ddmd/ and just leave mars.d and things like idgen.d in the main src/ directory.
For demonstration purposes, here is a dub package to play around with:
http://code.dlang.org/packages/ddmd-experimental
Here is the dub.json that was used:
https://github.com/aneas/ddmd-experimental/blob/master/dub.j son
And here is an example program using this API, have fun!
(run with $ chmod +x file.d; ./file.d)
---
#!/usr/bin/env dub
/+ dub.sdl:
name "ddmd-example"
dependency "ddmd-experimental" version="~>2.71.2-b2dub"
+/
import std.path;
import std.stdio;
import std.string;
import ddmd.builtin;
import ddmd.dmodule;
import ddmd.dclass;
import ddmd.dsymbol;
import ddmd.expression;
import ddmd.func;
import ddmd.globals;
import ddmd.id;
import ddmd.identifier;
import ddmd.mtype;
import ddmd.visitor;
void main(string[] args) {
if(args.length != 2) {
writeln("prints top-level function and class declarations");
writeln("usage: ", args[0].baseName, " d-filepath");
return;
}
// Initialization
global._init();
global.params.isLinux = true;
Type._init();
Id.initialize();
Module._init();
Expression._init();
builtin_init();
// Read and parse specified module
auto id = Identifier.idPool(args[1].baseName.stripExtension);
auto m = new Module(args[1].toStringz, id, false, false);
m.read(Loc());
m.parse();
// Output
m.accept(new class Visitor {
extern(C++):
alias visit = Visitor.visit;
override void visit(Module m) {
foreach(i; 0 .. m.members.dim)
(*m.members)[i].accept(this);
}
override void visit(Dsymbol s) {
}
override void visit(FuncDeclaration fd) {
writeln("function ", fd.ident.toString);
}
override void visit(ClassDeclaration cd) {
writeln("class ", cd.ident.toString);
}
});
}