I think that it's a useful option to have on LPG2.
//EBNF to be viewed with https://www.bottlecaps.de/rr/ui
JikesPG_INPUT ::= Grammar
Grammar ::= //$Empty
Grammar ::= Grammar include_segment END_KEY_opt
Grammar ::= Grammar notice_segment END_KEY_opt
Grammar ::= Grammar define_segment END_KEY_opt
Grammar ::= Grammar terminals_segment END_KEY_opt
Grammar ::= Grammar export_segment END_KEY_opt
Grammar ::= Grammar import_segment END_KEY_opt
Grammar ::= Grammar softkeywords_segment END_KEY_opt
Grammar ::= Grammar eof_segment END_KEY_opt
Grammar ::= Grammar eol_segment END_KEY_opt
Grammar ::= Grammar error_segment END_KEY_opt
Grammar ::= Grammar recover_segment END_KEY_opt
Grammar ::= Grammar identifier_segment END_KEY_opt
Grammar ::= Grammar start_segment END_KEY_opt
Grammar ::= Grammar alias_segment END_KEY_opt
Grammar ::= Grammar names_segment END_KEY_opt
Grammar ::= Grammar headers_segment END_KEY_opt
Grammar ::= Grammar ast_segment END_KEY_opt
Grammar ::= Grammar globals_segment END_KEY_opt
Grammar ::= Grammar trailers_segment END_KEY_opt
Grammar ::= Grammar rules_segment END_KEY_opt
Grammar ::= Grammar types_segment END_KEY_opt
Grammar ::= Grammar dps_segment END_KEY_opt
include_segment ::= 'INCLUDE_KEY'
include_segment ::= 'INCLUDE_KEY' 'SYMBOL'
notice_segment ::= 'NOTICE_KEY'
notice_segment ::= notice_segment action_segment
define_segment ::= 'DEFINE_KEY'
define_segment ::= define_segment macro_name_symbol macro_segment
macro_name_symbol ::= 'MACRO_NAME'
macro_name_symbol ::= 'SYMBOL'
macro_segment ::= 'BLOCK'
terminals_segment ::= 'TERMINALS_KEY'
terminals_segment ::= terminals_segment terminal_symbol
terminals_segment ::= terminals_segment terminal_symbol produces name
export_segment ::= 'EXPORT_KEY'
export_segment ::= export_segment terminal_symbol
import_segment ::= 'IMPORT_KEY'
import_segment ::= 'IMPORT_KEY' 'SYMBOL' drop_command_list
drop_command ::= drop_symbols
drop_command ::= drop_rules
drop_command ::= 'DROPACTIONS_KEY'
drop_symbols ::= 'DROPSYMBOLS_KEY'
drop_symbols ::= drop_symbols 'SYMBOL'
drop_rules ::= 'DROPRULES_KEY'
drop_rules ::= drop_rules drop_rule
drop_rule ::= 'SYMBOL' produces rhs
drop_rule ::= 'SYMBOL' 'MACRO_NAME' produces rhs
drop_rule ::= drop_rule '|' rhs
drop_command_list ::= //$Empty
drop_command_list ::= drop_command_list drop_command
softkeywords_segment ::= 'SOFTKEYWORDS_KEY'
softkeywords_segment ::= softkeywords_segment terminal_symbol
softkeywords_segment ::= softkeywords_segment terminal_symbol produces name
error_segment ::= 'ERROR_KEY'
error_segment ::= 'ERROR_KEY' terminal_symbol
recover_segment ::= 'RECOVER_KEY'
recover_segment ::= recover_segment terminal_symbol
identifier_segment ::= 'IDENTIFIER_KEY'
identifier_segment ::= 'IDENTIFIER_KEY' terminal_symbol
eol_segment ::= 'EOL_KEY'
eol_segment ::= 'EOL_KEY' terminal_symbol
eof_segment ::= 'EOF_KEY'
eof_segment ::= 'EOF_KEY' terminal_symbol
terminal_symbol ::= 'SYMBOL'
terminal_symbol ::= 'MACRO_NAME'
alias_segment ::= 'ALIAS_KEY'
alias_segment ::= alias_segment 'ERROR_KEY' produces alias_rhs
alias_segment ::= alias_segment 'EOL_KEY' produces alias_rhs
alias_segment ::= alias_segment 'EOF_KEY' produces alias_rhs
alias_segment ::= alias_segment 'IDENTIFIER_KEY' produces alias_rhs
alias_segment ::= alias_segment 'SYMBOL' produces alias_rhs
alias_segment ::= alias_segment alias_lhs_macro_name produces alias_rhs
alias_lhs_macro_name ::= 'MACRO_NAME'
alias_rhs ::= 'SYMBOL'
alias_rhs ::= 'MACRO_NAME'
alias_rhs ::= 'ERROR_KEY'
alias_rhs ::= 'EOL_KEY'
alias_rhs ::= 'EOF_KEY'
alias_rhs ::= 'EMPTY_KEY'
alias_rhs ::= 'IDENTIFIER_KEY'
start_segment ::= 'START_KEY'
start_segment ::= start_segment start_symbol
headers_segment ::= 'HEADERS_KEY'
headers_segment ::= headers_segment headers_action_segment_list
headers_action_segment_list ::= action_segment
headers_action_segment_list ::= headers_action_segment_list action_segment
ast_segment ::= 'AST_KEY'
ast_segment ::= ast_segment action_segment
globals_segment ::= 'GLOBALS_KEY'
globals_segment ::= globals_segment action_segment
trailers_segment ::= 'TRAILERS_KEY'
trailers_segment ::= trailers_segment action_segment
start_symbol ::= 'SYMBOL'
start_symbol ::= 'MACRO_NAME'
rules_segment ::= 'RULES_KEY' action_segment_list
rules_segment ::= rules_segment rules
rules ::= 'SYMBOL' produces rhs
rules ::= 'SYMBOL' 'MACRO_NAME' produces rhs
rules ::= 'SYMBOL' 'MACRO_NAME' 'MACRO_NAME' produces rhs
rules ::= rules '|' rhs
produces ::= '::='
produces ::= '::=?'
produces ::= '->'
produces ::= '->?'
rhs ::= //$Empty
rhs ::= rhs 'SYMBOL'
rhs ::= rhs 'SYMBOL' 'MACRO_NAME'
rhs ::= rhs 'EMPTY_KEY'
rhs ::= rhs rhs_action_segment_list
rhs_action_segment_list ::= action_segment
rhs_action_segment_list ::= rhs_action_segment_list action_segment
action_segment ::= 'BLOCK'
types_segment ::= 'TYPES_KEY'
types_segment ::= types_segment type_declarationlist
type_declarationlist ::= type_declarations
| type_declarations 'BLOCK'
type_declarations ::= 'SYMBOL' produces 'SYMBOL'
| type_declarations '|' 'SYMBOL'
dps_segment ::= 'DISJOINTPREDECESSORSETS_KEY'
dps_segment ::= dps_segment 'SYMBOL' 'SYMBOL'
names_segment ::= 'NAMES_KEY'
names_segment ::= names_segment name produces name
name ::= 'SYMBOL'
name ::= 'MACRO_NAME'
name ::= 'EMPTY_KEY'
name ::= 'ERROR_KEY'
name ::= 'EOL_KEY'
name ::= 'IDENTIFIER_KEY'
END_KEY_opt ::= //$Empty
END_KEY_opt ::= 'END_KEY'
action_segment_list ::= //$Empty
action_segment_list ::= action_segment_list action_segment
void Grammar::GenEBNF(void)
{
//
// First, flush any data left in the report buffer.
//
FILE *f = option -> syslis;
//
// Print the Rules
//
fprintf(f, "\n\n//EBNF to be viewed with https://www.bottlecaps.de/rr/ui\n\n");
{
int alternate_space = 0;
char64_t buf;
//
// Print the user specified rules.
//
for (int rule_index = start_symbol.Length(); rule_index <= num_rules; rule_index++)
{
int source_index = rules[rule_index].source_index;
if (rules[rule_index].IsAlternateProduction())
{
for (int i = 0; i < alternate_space; i++)
putc(' ', f);
putc(option -> or_marker, f);
}
else
{
const char *lhs_name = lex_stream -> NameString(parser.rules[source_index].lhs_index);
alternate_space = strlen(lhs_name) + (rules[rule_index].IsArrowProduction() ? 3 : 4);
DisplaySymbol(lhs_name);
int classname_index = parser.rules[source_index].classname_index,
array_element_type_index = parser.rules[source_index].array_element_type_index;
if (classname_index != 0 && array_element_type_index == 0)
fprintf(f, "%s", lex_stream -> NameString(classname_index));
else if (classname_index == 0 && array_element_type_index != 0)
fprintf(f, "%c%c%s", option -> macro_prefix,
option ->macro_prefix,
lex_stream -> NameString(classname_index));
else if (classname_index != 0 && array_element_type_index != 0)
fprintf(f, "%s%c%s", lex_stream -> NameString(classname_index),
option ->macro_prefix,
lex_stream -> NameString(array_element_type_index));
else assert (classname_index == 0 && array_element_type_index == 0);
if (rules[rule_index].IsArrowProduction())
fprintf(f, " ::= /*->*/ ");
else fprintf(f, " ::=");
}
for (int j = lex_stream -> Next(parser.rules[source_index].separator_index);
j < parser.rules[source_index].end_rhs_index;
j = lex_stream -> Next(j))
{
if (lex_stream -> Kind(j) == TK_SYMBOL) {
VariableSymbol *rhs_symbol = GetSymbol(j);
int image = (rhs_symbol ? AssignSymbolIndex(rhs_symbol) : 0);
if (image && IsTerminal(image))
fprintf(f, " '%s'", lex_stream -> NameString(j));
else
DisplaySymbol(lex_stream -> NameString(j));
}
else if (lex_stream -> Kind(j) == TK_MACRO_NAME)
fprintf(f, " %s", lex_stream -> NameString(j));
else if (lex_stream -> Kind(j) == TK_EMPTY_KEY)
fprintf(f, " //%c%s", option -> escape, "Empty");
}
putc('\n', f);
}
putc('\n', f); // leave a gap before listing the remaining rules.
}
return;
}