Creating srcML

Let’s start off by making a simple srcML unit. We have a C++ file called rotate.cpp. To transform this file to the srcML format, execute srcml on the command line with the path to the file.

$ srcml rotate.cpp

Because we have not specified an output location, srcml will output straight to the command line. You should see the contents of rotate.cpp marked up in the srcML format. If you used the contents of the file as shown in the provided link, you will see something like this:

<s:unit xmlns="http://www.srcML.org/srcML/src" xmlns:cpp="http://www.srcML.org/srcML/cpp" revision="1.0.0" language="C++" filename="src/rotate.cpp">
<cpp:include>#<cpp:directive>include</cpp:directive> <cpp:file>"rotate.h"</cpp:file></cpp:include>

<s:comment type="line">// rotate three values</s:comment>
<s:function><s:type><s:name>void</s:name></s:type> <s:name>rotate</s:name><s:parameter_list>(<s:parameter><s:decl><s:type><s:name>int</s:name><s:modifier>&amp;</s:modifier></s:type> <s:name>n1</s:name></s:decl></s:parameter>, <s:parameter><s:decl><s:type><s:name>int</s:name><s:modifier>&amp;</s:modifier></s:type> <s:name>n2</s:name></s:decl></s:parameter>, <s:parameter><s:decl><s:type><s:name>int</s:name><s:modifier>&amp;</s:modifier></s:type> <s:name>n3</s:name></s:decl></s:parameter>)</s:parameter_list>
<s:block>{<s:block_content>
  <s:comment type="line">// copy original values</s:comment>
  <s:decl_stmt><s:decl><s:type><s:name>int</s:name></s:type> <s:name>tn1</s:name> <s:init>= <s:expr><s:name>n1</s:name></s:expr></s:init></s:decl>, <s:decl><s:type ref="prev"/><s:name>tn2</s:name> <s:init>= <s:expr><s:name>n2</s:name></s:expr></s:init></s:decl>, <s:decl><s:type ref="prev"/><s:name>tn3</s:name> <s:init>= <s:expr><s:name>n3</s:name></s:expr></s:init></s:decl>;</s:decl_stmt>

  <s:comment type="line">// move</s:comment>
  <s:expr_stmt><s:expr><s:name>n1</s:name> <s:operator>=</s:operator> <s:name>tn3</s:name></s:expr>;</s:expr_stmt>
  <s:expr_stmt><s:expr><s:name>n2</s:name> <s:operator>=</s:operator> <s:name>tn1</s:name></s:expr>;</s:expr_stmt>
  <s:expr_stmt><s:expr><s:name>n3</s:name> <s:operator>=</s:operator> <s:name>tn2</s:name></s:expr>;</s:expr_stmt>
</s:block_content>}</s:block></s:function>
</s:unit>

You may be wondering, what happened to the original source-code? No worries, it’s all still there. If you were to remove all of the elements and attributes from this output, keeping only the text, you would see it matches the original source-code exactly. The extra content shown here are srcML tags, which identify the different syntactic parts of the source-code. srcml detects that rotate.cpp was written in C++ based on its cpp extension, and it leverages the knowledge of C++ syntax to identify the various elements of the code.

For example, we can break down the preprocessor statement #include "rotate.h" down to the include directive and file that was included. These are captured in the tags <cpp:include>, <cpp:directive>, and <cpp:file>. Notice that the contents of <cpp:include> contain the entire include statement, while <cpp:directive> contains only include, and <cpp:file> contains only the file name. This hierachical representation carries across all elements, such that all source-code within a block are contained within the <block> tags, all source-code within a function definition is contained within the <function> tags, all source-code within an expression is contained within the <expr> tags, etc. This allows for exploration and manipulation on a syntactic and hierarchical level.

You may also notice some extra attributes at the top of the srcML file denoting the name of the file that was parsed, its language, and a revision.

Outputting to a file can be accomplished with the -o option, or by piping standard output to a file. For example, the following command will output the resulting srcML format to a rotate.xml file. The xml extension is important in order to parse it as a srcML file later, and all srcML is valid XML.

$ srcml rotate.cpp -o rotate.xml

Now that we have a srcML file to work with, let’s transform it back to source code! This is easily accomplished the same way that we transformed it to srcML, by giving srcml the name of the file.

$ srcml rotate.xml

Similarly, an output file such as rotate.cpp can be specified with the output option, or if left without, srcml will output to standard out. Here, srcml knows to transform from the srcML format to source-code because of the xml extension on the input file.

The same commands apply for project directories, archives, and compressed files. Let’s give it a try with an example C++ project called narq, which has four files in it. Download the tar.gz for the project here, and run srcml on it with the following command:

$ srcml --verbose narq.tar.gz -o narq.xml

Because we ran it with the verbose option, we can see each of the files that were parsed along with some information about each file. This is helpful for verification, and as a progress indicator, when running srcml on a project directory or archive that contains many files. Let’s talk about the verbose output first, which looks like:

XML encoding:  UTF-8
    -                                                     narq/Makefile
    1 C++     89 ff24de1d2589a14b152c7687bd7e790a95cf123e narq/main.cpp
    2 C++     85 e53c55d4c3e07e514baeb4279222822b83ad1367 narq/tools.cpp
    3 C++     29 8422ea3439d538bdfb1fcca8ee8d8739bff3d64f narq/tools.hpp

Source Files: 3	Other Files: 1	Errors: 0	Total Files: 4

Some basic summary information is displayed here, including a list of the files provided to srcml, the programming language that file was written in, the number of lines in the file, and a unique SHA-1 hash computed based on the contents of the source-code file. At the end, we see that all but one of the files from our example project were parsed. This is because any file from a directory or archive that does not have a supported source-code file extension is skipped, such as XML, HTML, or Make files, and files written in programming languages that srcml does not support. The programming languages that srcml does support are files written in C, C++, C#, and Java.

Now let’s look at narq.xml, where we output the srcML file. There’s a lot of information here, but to summarize you should see the output follow the format:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<unit xmlns="http://www.srcML.org/srcML/src" revision="1.0.0">

<unit revision="1.0.0" language="C++" filename="narq/main.cpp" hash="ff24de1d2589a14b152c7687bd7e790a95cf123e">
...
</unit>

<unit revision="1.0.0" language="C++" filename="narq/tools.cpp" hash="e53c55d4c3e07e514baeb4279222822b83ad1367">
...
</unit>

<unit revision="1.0.0" language="C++" filename="narq/tools.hpp" hash="8422ea3439d538bdfb1fcca8ee8d8739bff3d64f">
...
</unit>

</unit>

Where each ... contains srcML based on the content of that file. Notice that all of the files are condensed into one srcML archive. At the root unit, the src namespace appears, which is used to mark up all srcML files regardless of language. We also have a new attribute, url, which shows the name of the original tar.gz containing the source-code. Similarly to the simple srcML unit we made when we ran srcml on one file, for each file parsed there is exists a srcML unit with the namespaces used for the tags that markup the source-code file, the revision of srcml used to created the unit, the language of the file, and the name of the original source-code file.

In this example, we used a tar.gz file, but any tar, bz2, gz, zip, or cpio compression/archive input format is also supported. In addition, if we were to decompress narq.tar.gz to its original directory structure, we can also give srcml the narq directory and expect the same output.

We can also go from this srcML archive format back to the original source-code. A major difference here is that there are multiple files, so we have two options: 1) convert just one of the units back to source-code, or 2) convert all of the units back to source-code in their original directory heirarchy.

With the first option, we provide srcml with a specific unit to convert back to source-code. This is done with the unit option, which takes a unit number to extract. For example,

$ srcml --unit 1 narq.xml

will extract the first unit in the srcML archive and convert it back to source-code, writing to standard output. This is most useful when we only want one of the files or for testing the result of a transformation.

For the second option, we instruct srcml to extract all of the source-code files by recreating the original directory structure. This is accomplished with the to-dir option, which takes a path where the original directory structure will be recreated. This structure is based on the filename attribute for each of the srcML units. This means that files that have been skipped by srcml are not recreated, and srcML units whose filename attribute has been modified by some transformation will be recreated in the likeness of the new filename.

To demonstrate, let us extract the source-code from narq.xml with the following command:

$ srcml --to-dir . narq.xml

Here we’re instructing srcml to extract the source-code contents of narq.xml to the current directory. Apart from the directories in the project that will be extracted, srcml will not create a directory if it doesn’t exist. This means that the path given to the to-dir option must exist. As a result of this operation, you will see the following directory structure in the current directory, where the contents of the files match the original source-code:

narq/
    main.cpp
    tools.cpp
    tools.hpp