The milestone was to completely eliminate the libdparse
dependency from dfmt, and implement 4 transformations using the AST traversal.
DMD provides two implementations of the language AST - ASTBase
and ASTCodegen
. The compiler itself uses the latter, which is generated by the parser, and later enriched by semantic passes. The former was created as a way to provide the AST through the DMD library without requiring the unnecessary semantic information that ASTCodegen
provides. While this is ideal for a formatter, the classes for many nodes in ASTBase
did not contain certain key methods required to make the formatter work (notably, toChars()
and toString()
). The dmd.hdrgen
package implements these helper methods for ASTCodegen
and uses it for header generation through dmd -H
. This means that using ASTCodegen
largely reduces the burden of manually adding additional helper methods for each node in dfmt and maintaining it. The decision was taken to proceed with using ASTCodegen
in dfmt, and replacing it with ASTBase
in the future if necessary.
A non-trivial amount of time was also spent in debugging why the AST was not being built properly in the parsing phase. Certain bugs like the FuncDeclaration
node having a valid declaration but no body were difficult to debug, mainly due to not being able to isolate the source of the issue. After a considerable amount of hunting, the process of initialising the frontend in DMD was fixed, and the standalone dmd.frontend.parseModule()
function was used to parse the input file instead of dmd.dmodule.Module.parse()
. This got rid of most bugs that were previously present.
The driver code in main.d
originally used appender!string
from std.array
as a buffer to write the output to. The D string
type does not have an equivalent in C++, restricting us from using it with extern (C++)
classes. To mitigate this, the driver code was modified to use std.file.File.LockingTextWriter
instead of appender!string
for writing the formatted output.
Multiple changes were introduced in the commit that added the AST into the formatter, mostly to ensure that the input file is parsed correctly. The logic for node traversal was added next, followed by the indentation logic. This provided us with a fully working implementation of dfmt that could take an input file, walk the AST, and output the formatted file. It was now possible to begin adding the transformation passes.
The following four transformations have been implemented:
dfmt_space_before_function_parameters
: Adds a space before the opening parenthesis in function declarations, e.g.int foo (int a, int b) {}
. This is disabled by default.dfmt_space_after_cast
: Adds a space between the cast and the value being cast, e.g.cast(int) 0
. This is enabled by default.dfmt_align_switch_statements
: Aligns the case statements and labels inside aswitch
block at the same level rather than one level deeper. This is enabled by default.dfmt_space_before_aa_colon
: Adds a space before the colon in associative array key-value pairs, e.g.auto a = { "Hello" : "Hi" };
. This used to be the old behaviour of dfmt, and is disabled by default now.
- feat: make it compile with dmd AST (amended)
- feat: implement visitors and writers
- feat: add indentation logic
- fix: iron out some bugs
- feat: add 4 transformations
The next milestone involves adding 7 more transformations using the AST.