Trash has been extended to parse and execute for-loops of XPath2.0. It also has been extended with attributes for line and column of a parse tree node. These extensions now allow one to create a script to find useless parentheses in an Antlr4 grammar, and print the location of those parentheses.

find-useless.sh

#!/bin/bash
export MSYS2_ARG_CONV_EXCL="*"
count=`trparse $1 | trxgrep -e 'for $i in (//(altList | labeledAlt)/alternative/element[ebnf[not(child::blockSuffix)]/block/altList[not(@ChildCount > 1)]] | //(altList | labeledAlt)[not(@ChildCount > 1)]/alternative[not(@ChildCount > 1)]/element[ebnf[not(child::blockSuffix)]/block/altList[@ChildCount > 1]] | //ebnf/block[altList[@ChildCount = 1]/alternative[@ChildCount = 1]/element/atom]) return concat("line ", $i/@Line, " col ", $i/@Column, " """, $i/@Text,"""")'`
if [ "$count" != "0" ]
then
	trparse $1 | trxgrep -e 'for $i in (//(altList | labeledAlt)/alternative/element[ebnf[not(child::blockSuffix)]/block/altList[not(@ChildCount > 1)]] | //(altList | labeledAlt)[not(@ChildCount > 1)]/alternative[not(@ChildCount > 1)]/element[ebnf[not(child::blockSuffix)]/block/altList[@ChildCount > 1]] | //ebnf/block[altList[@ChildCount = 1]/alternative[@ChildCount = 1]/element/atom]) return concat("line ", $i/@Line, " col ", $i/@Column, " """, $i/@Text,"""")' | sort -u
else
	echo No useless parentheses.
fi

Input grammar t1.g4

grammar g1;
a : ('a');
b : ( 'b' ) | a;
c : b | (a);
d : (b | c);

Command

bash find-useless.sh t1.g4 | sort -u

Output

line 2 col 4 "('a')"
line 3 col 4 "('b')"
line 4 col 8 "(a)"
line 5 col 4 "(b|c)"

find-useless.sh uses node set union operators ('|'). I don’t know how to create a list of nodes that are unique with XPath, so I perform a sort -u of the output.

Note: For context, the Antlr grammar is parsed using this grammar. You can see the parse tree of the grammar at a command prompt: trparse g1.g4 | trtree.