Author: Ken Domino

Ken is a software engineer and has worked professionally in the field since 1984. He has a masters of science in computer engineering and a masters of science in biology. For more information, please see http://kennethdomino.com.

I’ve made another release of Antlrvsix, version 7.3, last week and I’m almost ready to release 7.4 with a few more fixes. The changes I’ve been making since last year in August have greatly enhanced what Antlrvsix can do over version 1. But, there is more work to be done. I’m now working on updating and integrating Piggy into the extension.

Part of the problem with Piggy was that I lacked a clear vision of what I was trying to solve. I now know what I’m looking to solve because I have worked on the analysis and transformations for Antlr. And, I can see there are actually two levels to this system: (a) a high-level language, that works with snippets in the language that is going to be modified, and (b) a low-level language, that works with tree patterns and routines to manipulate those trees. This was what the authors of Coccinelle did, and it is the right idea because it’s hard writing tree patterns in a language like XPath, which is just a language that specifies a collection of tree nodes.

What is the next step in Antlrvsix to help improve a grammar? To answer that, I decided to go back and take a look at where this all began, with the comparison of the Java grammars in Antlr.

Java9 is derived from version 9 of the Java spec. Parr’s Java is derived from an unknown, earlier version of the spec, but it is much faster in parsing that the other grammar.

How can one compare these grammars? A line-by-line diff of the files could be done, but it is a terrible way to compare the grammars because they are very different: one is a partitioned grammar, the other a combined grammar; the formatting is different; the rules in each grammar are ordered differently; and, the rules differ considerably between the grammars. In order to compare the grammars, a much smarter diff is needed.

What I’m planning is to do a rule-by-rule comparison, working with rules in a DFS order from the start rule. I’ll need to know how the rules differ and why. Then, using Antlrvsix, apply a transformation to make the rules the same, and continue the diff with the next rule in the DFS ordering.

So, I first applied a DFS reorder of the grammars using the Antlrvsix extension, then a reformat (it turns out Codebuff has some bugs). Starting with the start rules, compilationUnit, the difference has already started: Parr decided to unfold ordinaryCompilation and remove the rule.

I could edit the grammar manually for the rule, but I decided to save time and implement the fold transformation right now. And, in order to make this reproducible, I want Antlrvsix to take a sequence of transformations from a file and apply them.

I’ve made a release of Antlrvsix yesterday which includes recursion removal for Antlr grammars. There are two types of recursion that I can work with: direct (aka immediate) and indirect. The algorithm that I use for direct and indirect recursion removal are the ones that Aho et al. (2006) describe. In addition, the extension provides a transform to rewrite direct recursion (left or right) into Kleene operator form, which helps clean up the grammar considerably. These transformations help fix grammar problems that you may run across. But, to use these transformations, you have to select the rule that contains or participates in the recursion.

In addition, the extension now provides an explicit mapping for colorizing the grammar. The problem with LSP and VS2019 is that neither supports languages like Antlr. So, language features like nonterminals and terminals are mapped to client-specific data types that have display properties. This mapping can be modified in the options file for Antlrvsix. In addition, I now tag almost all of the grammar.

What’s next up for Antlrvsix? The main problem I wanted to solve is the performance of the Java9 parser, which performs terribly for certain input. The issue seems to involve left factoring, but it’s not clear. I’ll now be working on this for the next month. I was hoping to add Piggy into the extension, but that will have to wait.

After a bit of fiddling around with what I thought would be a lexical analyzer for Flex, I now realize that the lexer isn’t defined by scan.l, but a combination of one generated from flex.skl, a file that is processed by m4, and the scanner generated by scan.l. Originally, flex.skl included some switches for C or C++, but it’s now m4 dependent. I’m not sure why #ifdefs wouldn’t have sufficed; it would have been far more portable. Further, after carefully inspecting the parser grammar, I now realize it is missing a ton of things from an input file because the lexer yanks so much information out of the input stream and never passes it onto the parser. For example, most of the options in a Flex file are simply swallowed by the scanner, only the ones with ‘=’ passed to the parser. (Bullshit.) Code blocks within %{ … %} or %top{ … } are swallowed up by the lexer, and never mentioned in the parser grammar. So, in order to really write an import of Flex, the lexer and scanner would have to be written from scratch. (Originally, I was toying with doing just that, after successfully converting Bison’s parse.y to Antlr, but chose to start with the Flex sources because I thought the grammars were complete. They just were complete garbage from the standpoint of an import.) Given the number of problems, I’m going to pass on Flex import for now. I will probably revisit Flex import but at a later date. For now, I’m ODed on a fruitless translation.

Fortunately, I don’t really need to implement any of this for Antlrvsix right now. I’m going to continue with the rest of the “to do’s” I have slated.

I’ve written a basic Bison import for Antlrvsix, and now I’m in the guts of writing a Flex import. While writing the Bison import, I soon realized a problem that I could not deal with immediately. Many people write Bison code that is very undisciplined. Often the code contains driver code (calc.y from the official Gnu repository), The static semantics is often wrapped up with the tree construction. While this is ok, this means the grammar contains target-language code, which is not a great way to write grammars. If you really want an abstract syntax tree instead of a parse tree for semantic checks, you should discipline the semantic checks outside of tree construction. For now, I do not copy code blocks to the generated Antlr grammar.

For Flex, the problem is that the embedded code exclusively performs semantic checks at the lexer phase. So this code must be included in a generated Antlr lexer grammar. The good news is that it’s not too difficult to convert the patterns in Flex into Antlr lexer rules and modes. You can then convert the code blocks in a Flex rule into your target language. Whether this works or not, I will find out–I’m translating line-by-line the Flex lexical grammar into an Antlr lexer grammar, and so far, it looks good.

Aside, I’ve been using Antlrvsix quite a bit myself to write the Flex grammars. I added a refactoring to sort lexer modes alphabetically since the original Flex “scan.l” code defined the start states in a haphazard order. And, since I’ve switch to the “Dark mode” theme in Visual Studio 2019, I added to the options color selections for grammar symbols so I can see the symbol text more clearly.

This is a placeholder for something I’ve been wanting to do for a while. I’m constantly seeing Antlr grammars posted in Stackoverflow that I want to test out. So, I’d like Antlr4BuildTasks.Templates to include something where you can just copy the grammar files into a directory, then type “dotnet new antlr”, and the damn thing will create a driver for the grammars. Another thing I’d like is to type “dotnet new antlr –grammar Java9” and it fish out the grammar files from the Antlr Grammars-v4 Github repository, then create a damn driver. This should be automated.

While I hunker down here for SARS-CoV-19 to burn itself out (I am in a very high-risk category), I decided to check up on the activity of commits between Antlr and Bison. This is because I read on the Internet that Bison isn’t being actively developed. It turns out, not only is this not true, Bison is being modified more often than Antlr. I wonder what changes are being made to Bison. And, I wonder why activity for Antlr is so low.

At this point, I am working on the code for refactoring Antlr grammars in Antlrvsix. While I plan to use Piggy to implement these refactorings, I will first implement them in C# code so I can get a clear picture of what to do with Piggy, make any changes, then rewrite the refactorings using Piggy templates.

This is just a note to myself on what procedures I take to clean up source code. This occurs because of the basic development cycle in trying to get something to work with complicated APIs, which goes something like this:

I search the web to find something that supposedly works, i.e., it was written by someone of authority, e.g. Microsoft, and was checked into Github.

I copy and paste snippets into my code and try it out.

It usually doesn’t work because the APIs were changed or gone obsolete, or I’m trying to add in something that isn’t compatible with other code. If it doesn’t work, I go back and try something else.

After a bunch of iterations of adding in useless code, I find something that works and check it in.

The first step is to reorder all the garbage so that classes have elements of fields, properties, and methods ordered, and each element is ordered alphabetically. To reorganize, select the document, then right-click and select “CodeMaid->Reorganize Active Document”. Note, if you have preprocessor directives, CodeMaid will not observe them in the reorganization. You should push any pragmas into the project .csproj file.

Test using “Find all references” and remove manually any unused fields, properties, methods, and types. Note, take care to not remove any unused elements or classes that APIs are expecting.

Set tabs to 4 spaces, and replace them with spaces.

I personally do not care for most code document comments and usually strip all comments from code. They clutter the entire meaning of the code. The code should speak for itself. If you can’t understand your code, you should not program.

Strip all #regions. It is a stupid concept and a waste of a compiler directive. It is better to use a proper development environment that does this for you.

1/2: Adding to #Antlrvsix an analysis tool of #Antlr grammars. This is how it works with cycle detection and useless lexer rules. There are some issues in the responsiveness of the MS LSP client for VS2019.

Adding to #Antlrvsix the refactoring to remove useless parentheses in an #Antlr grammar. This is how it works with the extra parentheses in the arrayAccess_lf_primary rule in Java9.g4 that nobody knew were there. Only yet starting to scratch the surface of grammar optimizations.

Implementing #Antlr grammar fold refactorings in #Antlrvsix. Two types: extract a selected sequence of symbols and make a rule (shown first); replace all occurrences in the grammar with a folded rule (shown second). Spacing and comments do not matter.