Added basic XML output

This commit is contained in:
Hackerpilot 2013-07-23 01:46:08 +00:00
parent a5fd3efdb8
commit 1aec76fdea
4 changed files with 180 additions and 48 deletions

72
astprinter.d Normal file
View File

@ -0,0 +1,72 @@
import std.d.lexer;
import std.d.ast;
import std.stdio;
class XMLPrinter : ASTVisitor
{
override void visit(Module mod)
{
output.writeln("<module>");
mod.accept(this);
output.writeln("</module>");
}
override void visit(ModuleDeclaration modDec)
{
output.writeln("<moduleDeclaration>");
modDec.accept(this);
output.writeln("</moduleDeclaration>");
}
override void visit(IdentifierChain chain)
{
output.writeln("<identifierChain>");
foreach (ident; chain.identifiers)
{
output.writeln("<identifier>", ident.value, "</identifier>");
}
output.writeln("</identifierChain>");
}
override void visit(ClassDeclaration classDec)
{
output.writeln("<classDeclaration line=\"", classDec.name.line, "\">");
output.writeln("<name>", classDec.name.value, "</name>");
output.writeln("</classDeclaration>");
}
override void visit(StructDeclaration structDec)
{
output.writeln("<structDeclaration line=\"", structDec.name.line, "\">");
output.writeln("<name>", structDec.name.value, "</name>");
output.writeln("</structDeclaration>");
}
override void visit(FunctionDeclaration functionDec)
{
output.writeln("<functionDeclaration line=\"", functionDec.name.line, "\">");
output.writeln("<name>", functionDec.name.value, "</name>");
output.writeln("</functionDeclaration>");
}
override void visit(EnumDeclaration enumDec)
{
output.writeln("<enumDeclaration line=\"", enumDec.name.line, "\">");
if (enumDec.name.type == TokenType.identifier)
output.writeln("<name>", enumDec.name.value, "</name>");
enumDec.accept(this);
output.writeln("</enumDeclaration>");
}
override void visit(EnumMember enumMem)
{
output.writeln("<enumMember line=\"", enumMem.name.line, "\">");
output.writeln("<name>", enumMem.name.value, "</name>");
enumMem.accept(this);
output.writeln("</enumMember>");
}
alias ASTVisitor.visit visit;
File output;
}

26
main.d
View File

@ -21,6 +21,7 @@ import std.d.parser;
import highlighter; import highlighter;
import stats; import stats;
import ctags; import ctags;
import astprinter;
int main(string[] args) int main(string[] args)
{ {
@ -28,19 +29,20 @@ int main(string[] args)
bool sloc; bool sloc;
bool highlight; bool highlight;
bool ctags; bool ctags;
bool json;
bool recursive; bool recursive;
bool format; bool format;
bool help; bool help;
bool tokenCount; bool tokenCount;
bool syntaxCheck; bool syntaxCheck;
bool ast;
try try
{ {
getopt(args, "I", &importDirs, "sloc|l", &sloc, getopt(args, "I", &importDirs, "sloc|l", &sloc,
"json|j", &json, "highlight", &highlight, "highlight", &highlight,
"ctags|c", &ctags, "recursive|r|R", &recursive, "help|h", &help, "ctags|c", &ctags, "recursive|r|R", &recursive, "help|h", &help,
"tokenCount", &tokenCount, "syntaxCheck|s", &syntaxCheck); "tokenCount", &tokenCount, "syntaxCheck|s", &syntaxCheck,
"ast|xml", &ast);
} }
catch (Exception e) catch (Exception e)
{ {
@ -53,8 +55,8 @@ int main(string[] args)
return 0; return 0;
} }
auto optionCount = count!"a"([sloc, highlight, ctags, json, tokenCount, auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount,
syntaxCheck]); syntaxCheck, ast]);
if (optionCount > 1) if (optionCount > 1)
{ {
stderr.writeln("Too many options specified"); stderr.writeln("Too many options specified");
@ -104,6 +106,13 @@ int main(string[] args)
{ {
parseModule(tokens.array(), args[1]); parseModule(tokens.array(), args[1]);
} }
else if (ast)
{
auto mod = parseModule(tokens.array(), args[1]);
auto printer = new XMLPrinter;
printer.output = stdout;
printer.visit(mod);
}
} }
return 0; return 0;
@ -123,10 +132,6 @@ options:
count the number of logical lines of code in the given count the number of logical lines of code in the given
source files. If no files are specified, a file is read from stdin. source files. If no files are specified, a file is read from stdin.
--json | -j [sourceFile]
Generate a JSON summary of the given source file. If no file is
specifed, the file is read from stdin.
--highlight [sourceFile] - Syntax-highlight the given source file. The --highlight [sourceFile] - Syntax-highlight the given source file. The
resulting HTML will be written to standard output. resulting HTML will be written to standard output.
@ -148,6 +153,9 @@ options:
ctags information requires a filename, so stdin cannot be used in place ctags information requires a filename, so stdin cannot be used in place
of a filename. of a filename.
--ast | --xml sourceFile
Generates an XML representation of the source files abstract syntax tree
--recursive | -R | -r directory --recursive | -R | -r directory
When used with --ctags, dscanner will produce ctags output for all .d When used with --ctags, dscanner will produce ctags output for all .d
and .di files contained within the given directory and its and .di files contained within the given directory and its

View File

@ -233,6 +233,14 @@ interface ASTNode
immutable string DEFAULT_ACCEPT = q{void accept(ASTVisitor visitor) {}}; immutable string DEFAULT_ACCEPT = q{void accept(ASTVisitor visitor) {}};
template visitIfNotNull(fields ...)
{
static if (fields.length > 1)
immutable visitIfNotNull = visitIfNotNull!(fields[0]) ~ visitIfNotNull!(fields[1..$]);
else
immutable visitIfNotNull = "if (" ~ fields[0].stringof ~ " !is null) visitor.visit(" ~ fields[0].stringof ~ ");";
}
abstract class ExpressionNode : ASTNode {} abstract class ExpressionNode : ASTNode {}
mixin template BinaryExpressionBody() mixin template BinaryExpressionBody()
@ -254,7 +262,14 @@ public:
class AliasDeclaration : ASTNode class AliasDeclaration : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
mixin(visitIfNotNull!(type, declarator));
foreach (initializer; initializers)
{
if (initializers !is null) visitor.visit(initializer);
}
}
/** */ Type type; /** */ Type type;
/** */ Declarator declarator; /** */ Declarator declarator;
/** */ AliasInitializer[] initializers; /** */ AliasInitializer[] initializers;
@ -264,7 +279,11 @@ public:
class AliasInitializer : ASTNode class AliasInitializer : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
if (type !is null) visitor.visit(type);
}
/** */ Token identifier; /** */ Token identifier;
/** */ Type type; /** */ Type type;
} }
@ -305,7 +324,14 @@ public:
class ArgumentList : ASTNode class ArgumentList : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
foreach (item; items)
{
if (item !is null)
visitor.visit(item);
}
}
/** */ AssignExpression[] items; /** */ AssignExpression[] items;
} }
@ -313,7 +339,11 @@ public:
class Arguments : ASTNode class Arguments : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
if (argumentList !is null)
visitor.visit(argumentList);
}
/** */ ArgumentList argumentList; /** */ ArgumentList argumentList;
} }
@ -321,7 +351,13 @@ public:
class ArrayInitializer : ASTNode class ArrayInitializer : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
foreach(init; arrayMemberInitializations)
{
if (init !is null) visitor.visit(init);
}
}
/** */ ArrayMemberInitialization[] arrayMemberInitializations; /** */ ArrayMemberInitialization[] arrayMemberInitializations;
} }
@ -784,30 +820,16 @@ public:
override void accept(ASTVisitor visitor) override void accept(ASTVisitor visitor)
{ {
if (importDeclaration !is null) visitor.visit(importDeclaration);
if (functionDeclaration !is null) visitor.visit(functionDeclaration); mixin(visitIfNotNull!(importDeclaration, functionDeclaration,
if (variableDeclaration !is null) visitor.visit(variableDeclaration); variableDeclaration, aliasThisDeclaration, structDeclaration,
if (aliasThisDeclaration !is null) visitor.visit(aliasThisDeclaration); classDeclaration, interfaceDeclaration, unionDeclaration,
if (structDeclaration !is null) visitor.visit(structDeclaration); enumDeclaration, aliasDeclaration, mixinDeclaration,
if (classDeclaration !is null) visitor.visit(classDeclaration); mixinTemplateDeclaration, unittest_, staticAssertDeclaration,
if (interfaceDeclaration !is null) visitor.visit(interfaceDeclaration); templateDeclaration, constructor,
if (unionDeclaration !is null) visitor.visit(unionDeclaration); destructor, staticConstructor, staticDestructor,
if (enumDeclaration !is null) visitor.visit(enumDeclaration); sharedStaticDestructor, sharedStaticConstructor,
if (aliasDeclaration !is null) visitor.visit(aliasDeclaration); conditionalDeclaration, pragmaDeclaration, versionSpecification));
if (mixinDeclaration !is null) visitor.visit(mixinDeclaration);
if (mixinTemplateDeclaration !is null) visitor.visit(mixinTemplateDeclaration);
if (unittest_ !is null) visitor.visit(unittest_);
if (staticAssertDeclaration !is null) visitor.visit(staticAssertDeclaration);
if (templateDeclaration !is null) visitor.visit(templateDeclaration);
if (constructor !is null) visitor.visit(constructor);
if (destructor !is null) visitor.visit(destructor);
if (staticConstructor !is null) visitor.visit(staticConstructor);
if (staticDestructor !is null) visitor.visit(staticDestructor);
if (sharedStaticDestructor !is null) visitor.visit(sharedStaticDestructor);
if (sharedStaticConstructor !is null) visitor.visit(sharedStaticConstructor);
if (conditionalDeclaration !is null) visitor.visit(conditionalDeclaration);
if (pragmaDeclaration !is null) visitor.visit(pragmaDeclaration);
if (versionSpecification !is null) visitor.visit(versionSpecification);
} }
/** */ Attribute[] attributes; /** */ Attribute[] attributes;
@ -873,7 +895,12 @@ public:
class Declarator : ASTNode class Declarator : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT);
override void accept(ASTVisitor visitor)
{
mixin(visitIfNotNull!(initializer));
}
/** */ Token name; /** */ Token name;
/** */ Initializer initializer; /** */ Initializer initializer;
} }
@ -931,7 +958,13 @@ public:
class EnumBody : ASTNode class EnumBody : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
foreach (member; enumMembers)
{
if (member !is null) visitor.visit(member);
}
}
/** */ EnumMember[] enumMembers; /** */ EnumMember[] enumMembers;
} }
@ -939,7 +972,10 @@ public:
class EnumDeclaration : ASTNode class EnumDeclaration : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
mixin(visitIfNotNull!(type, enumBody));
}
/** */ Token name; /** */ Token name;
/** */ Type type; /** */ Type type;
/** */ EnumBody enumBody; /** */ EnumBody enumBody;
@ -949,8 +985,11 @@ public:
class EnumMember : ASTNode class EnumMember : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
/** */ Token identifier; {
mixin(visitIfNotNull!(type, assignExpression));
}
/** */ Token name;
/** */ Type type; /** */ Type type;
/** */ AssignExpression assignExpression; /** */ AssignExpression assignExpression;
} }
@ -1416,7 +1455,10 @@ public:
class ModuleDeclaration : ASTNode class ModuleDeclaration : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
if (moduleName !is null) visitor.visit(moduleName);
}
/** */ IdentifierChain moduleName; /** */ IdentifierChain moduleName;
} }
@ -2196,7 +2238,11 @@ public:
class WhileStatement : ASTNode class WhileStatement : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
mixin(visitIfNotNull!(expression, statementNoCaseNoDefault));
}
/** */ Expression expression; /** */ Expression expression;
/** */ StatementNoCaseNoDefault statementNoCaseNoDefault; /** */ StatementNoCaseNoDefault statementNoCaseNoDefault;
} }
@ -2205,7 +2251,11 @@ public:
class WithStatement : ASTNode class WithStatement : ASTNode
{ {
public: public:
mixin(DEFAULT_ACCEPT); override void accept(ASTVisitor visitor)
{
mixin(visitIfNotNull!(expression, statementNoCaseNoDefault));
}
/** */ Expression expression; /** */ Expression expression;
/** */ StatementNoCaseNoDefault statementNoCaseNoDefault; /** */ StatementNoCaseNoDefault statementNoCaseNoDefault;
} }

View File

@ -2002,6 +2002,8 @@ class ClassFour(A, B) if (someTest()) : Super {}}c;
if (expect(TokenType.enum_) is null) return null; if (expect(TokenType.enum_) is null) return null;
if (currentIs(TokenType.identifier)) if (currentIs(TokenType.identifier))
node.name = advance(); node.name = advance();
else
node.name.line = tokens[index - 1].line; // preserve line number if anonymous
if (currentIs(TokenType.colon)) if (currentIs(TokenType.colon))
{ {
advance(); advance();
@ -2026,10 +2028,10 @@ class ClassFour(A, B) if (someTest()) : Super {}}c;
if (currentIs(TokenType.identifier)) if (currentIs(TokenType.identifier))
{ {
if (peekIsOneOf(TokenType.comma, TokenType.rBrace)) if (peekIsOneOf(TokenType.comma, TokenType.rBrace))
node.identifier = advance(); node.name = advance();
else if (peekIs(TokenType.assign)) else if (peekIs(TokenType.assign))
{ {
node.identifier = advance(); node.name = advance();
goto assign; goto assign;
} }
else else