diff --git a/astprinter.d b/astprinter.d
new file mode 100644
index 0000000..4d4a2fa
--- /dev/null
+++ b/astprinter.d
@@ -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("");
+ mod.accept(this);
+ output.writeln("");
+ }
+
+ override void visit(ModuleDeclaration modDec)
+ {
+ output.writeln("");
+ modDec.accept(this);
+ output.writeln("");
+ }
+
+ override void visit(IdentifierChain chain)
+ {
+ output.writeln("");
+ foreach (ident; chain.identifiers)
+ {
+ output.writeln("", ident.value, "");
+ }
+ output.writeln("");
+ }
+
+ override void visit(ClassDeclaration classDec)
+ {
+ output.writeln("");
+ output.writeln("", classDec.name.value, "");
+ output.writeln("");
+ }
+
+ override void visit(StructDeclaration structDec)
+ {
+ output.writeln("");
+ output.writeln("", structDec.name.value, "");
+ output.writeln("");
+ }
+
+ override void visit(FunctionDeclaration functionDec)
+ {
+ output.writeln("");
+ output.writeln("", functionDec.name.value, "");
+ output.writeln("");
+ }
+
+ override void visit(EnumDeclaration enumDec)
+ {
+ output.writeln("");
+ if (enumDec.name.type == TokenType.identifier)
+ output.writeln("", enumDec.name.value, "");
+ enumDec.accept(this);
+ output.writeln("");
+ }
+
+ override void visit(EnumMember enumMem)
+ {
+ output.writeln("");
+ output.writeln("", enumMem.name.value, "");
+ enumMem.accept(this);
+ output.writeln("");
+ }
+
+ alias ASTVisitor.visit visit;
+
+ File output;
+}
diff --git a/main.d b/main.d
index b431c3c..99a573c 100755
--- a/main.d
+++ b/main.d
@@ -21,6 +21,7 @@ import std.d.parser;
import highlighter;
import stats;
import ctags;
+import astprinter;
int main(string[] args)
{
@@ -28,19 +29,20 @@ int main(string[] args)
bool sloc;
bool highlight;
bool ctags;
- bool json;
bool recursive;
bool format;
bool help;
bool tokenCount;
bool syntaxCheck;
+ bool ast;
try
{
getopt(args, "I", &importDirs, "sloc|l", &sloc,
- "json|j", &json, "highlight", &highlight,
+ "highlight", &highlight,
"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)
{
@@ -53,8 +55,8 @@ int main(string[] args)
return 0;
}
- auto optionCount = count!"a"([sloc, highlight, ctags, json, tokenCount,
- syntaxCheck]);
+ auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount,
+ syntaxCheck, ast]);
if (optionCount > 1)
{
stderr.writeln("Too many options specified");
@@ -104,6 +106,13 @@ int main(string[] args)
{
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;
@@ -123,10 +132,6 @@ options:
count the number of logical lines of code in the given
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
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
of a filename.
+ --ast | --xml sourceFile
+ Generates an XML representation of the source files abstract syntax tree
+
--recursive | -R | -r directory
When used with --ctags, dscanner will produce ctags output for all .d
and .di files contained within the given directory and its
diff --git a/std/d/ast.d b/std/d/ast.d
index b92cf81..881674e 100755
--- a/std/d/ast.d
+++ b/std/d/ast.d
@@ -233,6 +233,14 @@ interface ASTNode
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 {}
mixin template BinaryExpressionBody()
@@ -254,7 +262,14 @@ public:
class AliasDeclaration : ASTNode
{
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;
/** */ Declarator declarator;
/** */ AliasInitializer[] initializers;
@@ -264,7 +279,11 @@ public:
class AliasInitializer : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ if (type !is null) visitor.visit(type);
+ }
+
/** */ Token identifier;
/** */ Type type;
}
@@ -305,7 +324,14 @@ public:
class ArgumentList : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ foreach (item; items)
+ {
+ if (item !is null)
+ visitor.visit(item);
+ }
+ }
/** */ AssignExpression[] items;
}
@@ -313,7 +339,11 @@ public:
class Arguments : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ if (argumentList !is null)
+ visitor.visit(argumentList);
+ }
/** */ ArgumentList argumentList;
}
@@ -321,7 +351,13 @@ public:
class ArrayInitializer : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ foreach(init; arrayMemberInitializations)
+ {
+ if (init !is null) visitor.visit(init);
+ }
+ }
/** */ ArrayMemberInitialization[] arrayMemberInitializations;
}
@@ -784,30 +820,16 @@ public:
override void accept(ASTVisitor visitor)
{
- if (importDeclaration !is null) visitor.visit(importDeclaration);
- if (functionDeclaration !is null) visitor.visit(functionDeclaration);
- if (variableDeclaration !is null) visitor.visit(variableDeclaration);
- if (aliasThisDeclaration !is null) visitor.visit(aliasThisDeclaration);
- if (structDeclaration !is null) visitor.visit(structDeclaration);
- if (classDeclaration !is null) visitor.visit(classDeclaration);
- if (interfaceDeclaration !is null) visitor.visit(interfaceDeclaration);
- if (unionDeclaration !is null) visitor.visit(unionDeclaration);
- if (enumDeclaration !is null) visitor.visit(enumDeclaration);
- if (aliasDeclaration !is null) visitor.visit(aliasDeclaration);
- 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);
+
+ mixin(visitIfNotNull!(importDeclaration, functionDeclaration,
+ variableDeclaration, aliasThisDeclaration, structDeclaration,
+ classDeclaration, interfaceDeclaration, unionDeclaration,
+ enumDeclaration, aliasDeclaration, mixinDeclaration,
+ mixinTemplateDeclaration, unittest_, staticAssertDeclaration,
+ templateDeclaration, constructor,
+ destructor, staticConstructor, staticDestructor,
+ sharedStaticDestructor, sharedStaticConstructor,
+ conditionalDeclaration, pragmaDeclaration, versionSpecification));
}
/** */ Attribute[] attributes;
@@ -873,7 +895,12 @@ public:
class Declarator : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+
+ override void accept(ASTVisitor visitor)
+ {
+ mixin(visitIfNotNull!(initializer));
+ }
+
/** */ Token name;
/** */ Initializer initializer;
}
@@ -931,7 +958,13 @@ public:
class EnumBody : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ foreach (member; enumMembers)
+ {
+ if (member !is null) visitor.visit(member);
+ }
+ }
/** */ EnumMember[] enumMembers;
}
@@ -939,7 +972,10 @@ public:
class EnumDeclaration : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ mixin(visitIfNotNull!(type, enumBody));
+ }
/** */ Token name;
/** */ Type type;
/** */ EnumBody enumBody;
@@ -949,8 +985,11 @@ public:
class EnumMember : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
- /** */ Token identifier;
+ override void accept(ASTVisitor visitor)
+ {
+ mixin(visitIfNotNull!(type, assignExpression));
+ }
+ /** */ Token name;
/** */ Type type;
/** */ AssignExpression assignExpression;
}
@@ -1416,7 +1455,10 @@ public:
class ModuleDeclaration : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ if (moduleName !is null) visitor.visit(moduleName);
+ }
/** */ IdentifierChain moduleName;
}
@@ -2196,7 +2238,11 @@ public:
class WhileStatement : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ mixin(visitIfNotNull!(expression, statementNoCaseNoDefault));
+ }
+
/** */ Expression expression;
/** */ StatementNoCaseNoDefault statementNoCaseNoDefault;
}
@@ -2205,7 +2251,11 @@ public:
class WithStatement : ASTNode
{
public:
- mixin(DEFAULT_ACCEPT);
+ override void accept(ASTVisitor visitor)
+ {
+ mixin(visitIfNotNull!(expression, statementNoCaseNoDefault));
+ }
+
/** */ Expression expression;
/** */ StatementNoCaseNoDefault statementNoCaseNoDefault;
}
diff --git a/std/d/parser.d b/std/d/parser.d
index 7d0157c..39b885e 100755
--- a/std/d/parser.d
+++ b/std/d/parser.d
@@ -2002,6 +2002,8 @@ class ClassFour(A, B) if (someTest()) : Super {}}c;
if (expect(TokenType.enum_) is null) return null;
if (currentIs(TokenType.identifier))
node.name = advance();
+ else
+ node.name.line = tokens[index - 1].line; // preserve line number if anonymous
if (currentIs(TokenType.colon))
{
advance();
@@ -2026,10 +2028,10 @@ class ClassFour(A, B) if (someTest()) : Super {}}c;
if (currentIs(TokenType.identifier))
{
if (peekIsOneOf(TokenType.comma, TokenType.rBrace))
- node.identifier = advance();
+ node.name = advance();
else if (peekIs(TokenType.assign))
{
- node.identifier = advance();
+ node.name = advance();
goto assign;
}
else