3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-12-27 05:02:34 +01:00
pbot/applets/c2english/CGrammar.pm
Pragmatic Software 3d97dc2c33 Rename "modules" to "applets"
"Applet" is a much better name for the external command-line
scripts and programs that can be loaded as PBot commands. They
will no longer be confused with Perl modules.

https://en.wikipedia.org/wiki/Applet
2021-11-19 18:05:50 -08:00

1962 lines
69 KiB
Perl
Executable File

# C-to-English Grammar
# Pragmatic Software
# SPDX-FileCopyrightText: 2021 Pragmatic Software <pragma78@gmail.com>
# SPDX-License-Identifier: MIT
{
my ($rule_name, @macros, @typedefs, @identifiers);
}
startrule:
translation_unit
{
my $output = $item[-1];
$output =~ s/\^L(\s*.?)/\L$1/g; # lowercase specified characters
$output =~ s/\^U(\s*.?)/\U$1/g; # uppercase specified characters
push @$return, $output;
}
startrule(?)
{ push @$return, $item[-1]; }
translation_unit:
comment
| external_declaration
| function_definition
| preproc[matchrule => 'translation_unit']
preproc:
'#' (definition
| undefinition
| inclusion
| line
| error
| pragma
| preproc_conditional[matchrule => $arg{matchrule}])
definition:
macro_definition
| 'define' identifier token_sequence(?) <skip: '[ \t]*'> "\n"
{
my $token_sequence = join('',@{$item{'token_sequence(?)'}});
$return = "Define the macro $item{identifier}";
$return .= " to mean $token_sequence" if $token_sequence;
$return .= ".\n";
}
macro_definition:
'define' identifier '(' <leftop: identifier ',' identifier> ')' token_sequence <skip: '[ \t]*'> "\n"
{
my @symbols = @{$item[-5]};
my $last;
push @macros, $item{identifier};
$return = "Define the macro $item{identifier} ";
if ($#symbols > 0) {
$last = pop @symbols;
$return .= "with the symbols " . join(", ",@symbols) . " and $last ";
} else {
$return .= "with the symbol $symbols[0] ";
}
$return .= "to use the token sequence `$item{token_sequence}`.\n";
}
undefinition:
'undef' identifier <skip: '[ \t]*'> "\n"
{
@macros = grep { $_ ne $item{identifier} } @macros;
"\nAnnul the definition of $item{identifier}.\n"
}
inclusion:
'include' '<' filename '>' <skip: '[ \t]*'> "\n"
{ "\nInclude the header $item{filename}.\n" }
| 'include' '"' filename '"' <skip: '[ \t]*'> "\n"
{ "\nInclude the source file $item{filename}.\n" }
| 'include' token
{ "\nImport code noted by the token $item{token}.\n" }
filename:
/[_\.\-\w\/]+/
line:
'line' constant ('"' filename '"'
{ "and filename $item{filename}" }
)(?) <skip: '[ \t]*'> "\n"
{ "\nThis is line number $item{constant} " . join('', @{$item[-3]}) . ".\n" }
error:
'error' token_sequence(?) <skip: '[ \t]*'> "\n"
{ "Stop compilation with error \"" . join('', @{$item{'token_sequence(?)'}}) . "\".\n" }
pragma:
'pragma' token_sequence(?) <skip: '[ \t]*'> "\n"
{
my $pragma = join('',@{$item{'token_sequence(?)'}});
if ($pragma) { $pragma = " \"$pragma\""; }
"Process a compiler-dependent pragma$pragma.\n"
}
preproc_conditional:
if_line[matchrule => $arg{matchrule}]
{ $rule_name = $arg{matchrule}; }
<matchrule: $rule_name>(s?)
{ $item{if_line} . join('',@{$item[-1]}) }
(elif_parts[matchrule => $rule_name])(?)
(else_parts[matchrule => $rule_name])(?)
{ join('',@{$item[-2]}) . join('',@{$item[-1]}) }
'#' 'endif'
{ "End preprocessor conditional.\n" }
if_line:
'ifdef' identifier <skip: '[ \t]*'> "\n"
{ "If the macro $item{identifier} is defined, then ^L" }
| 'ifndef' identifier <skip: '[ \t]*'> "\n"
{ "If the macro $item{identifier} is not defined, then ^L" }
| 'if' constant_expression <skip: '[ \t]*'> "\n"
{ "If the preprocessor condition^L $item{constant_expression} is true, then ^L" }
elif_parts:
('#' 'elif' constant_expression
{ "Otherwise, if the preprocessor condition $item{constant_expression} is true, then ^L" }
(<matchrule: $rule_name> )[matchrule => $arg{matchrule}](s?)
{ join('',@{$item[-1]}) }
)(s)
{ join('', @{$item[-1]}) }
else_parts:
'#' 'else'
{ $rule_name = $arg{matchrule}; }
(<matchrule: $rule_name>)[matchrule => $arg{matchrule}](s?)
{ "Otherwise, ^L" . join('',@{$item[-1]}) }
token_sequence:
token(s)
{ join(' ', @{$item[1]}) }
token:
<skip: '[ \t]*'> /\S+/
external_declaration:
declaration[context => 'external declaration']
function_definition:
declaration_specifiers[context => 'function definition'](?) declarator[context => 'function definition'] compound_statement[context => 'function definition statement'](?)
{
my $declaration_specifiers = join('', @{$item{'declaration_specifiers(?)'}});
my $name = $item{declarator}->[0];
my $parameter_list = $item{declarator}->[1];
my $return_type;
if (@{$item{declarator}} > 2) {
$return_type = "$item{declarator}->[2] $declaration_specifiers";
} else {
$return_type = $declaration_specifiers;
}
if ($return_type =~ s/( with.*)$//) {
my $specifier = $1;
$parameter_list =~ s/function/function$specifier/;
}
if ($return_type =~ s/inline//g) {
$return_type = join(' ', split(' ', $return_type));
my $and = $parameter_list =~ s/function with/function/ ? ' and' : '';
$parameter_list =~ s/function/function with suggestion to be as fast as possible$and/;
}
if ($return_type =~ s/_Noreturn//g) {
$return_type = join(' ', split(' ', $return_type));
$parameter_list =~ s/ returning$//;
if ($return_type eq 'void') {
$return_type = "which doesn't return to its caller";
} else {
$return_type = "which shouldn't return to its caller yet does mysteriously return $return_type";
}
}
if (ref $name eq 'ARRAY') {
my @a = @$name;
$name = shift @a;
$parameter_list = join(' ', @a) . " $parameter_list";
}
$return = "\nLet $name be a $parameter_list $return_type.\n";
my $statements = join('', @{$item{'compound_statement(?)'}});
$return .= "When called, the function will ^L$statements";
}
block_item_list:
block_item(s)
{
if (@{$item{'block_item(s)'}} == 1) {
$return = $item{'block_item(s)'}->[0];
} elsif (@{$item{'block_item(s)'}} == 2) {
my $first = $item{'block_item(s)'}->[0];
my $second = $item{'block_item(s)'}->[1];
$first =~ s/\.?\s*$//;
$return = "$first and then ^L$second";
} else {
my $last = pop @{$item{'block_item(s)'}};
$return = join('Then ^L', @{$item{'block_item(s)'}}) . "Finally, ^L$last";
}
}
block_item:
declaration
| statement[context => "$arg{context}|block item"]
| preproc
| comment
compound_statement:
'{' block_item_list(s?) '}'
{
my $block_items = join('', @{$item{'block_item_list(s?)'}});
if ($arg{context} =~ /block item/
and $arg{context} !~ /do loop$/
and $arg{context} !~ /if statement$/
and $arg{context} !~ /switch$/
and $arg{context} !~ /else block$/) {
$return = "Begin new block.\n";
}
if ($block_items) {
$return .= $block_items;
} else {
$return .= "Do nothing.\n";
}
if ($arg{context} =~ /block item/
and $arg{context} !~ /do loop$/
and $arg{context} !~ /if statement$/
and $arg{context} !~ /switch$/
and $arg{context} !~ /else block$/) {
$return .= "End block.\n";
}
if ($arg{context}
and $arg{context} !~ /do loop$/
and $arg{context} !~ /if statement$/
and $arg{context} !~ /else block$/
and $arg{context} !~ /case$/
and $arg{context} !~ /function definition statement$/
and $arg{context} !~ /function definition$/) {
my @contexts = split /\|/, $arg{context};
my $context = pop @contexts;
$return .= "End $context.\n" unless $context eq 'block item';
}
1;
}
statement_list:
comment(?) preproc[matchrule => 'statement'](?) statement[context => undef]
{
my $preproc = join('',@{$item{'preproc(?)'}});
my $comment = join('',@{$item{'comment(?)'}});
$return = $item{statement};
if ($comment) { $return = $comment . $return; }
if ($preproc) { $return = $preproc . $return; }
}
statement_list(?)
{ join('',@{$item{'statement_list(?)'}}) }
statement:
jump_statement
| compound_statement
| iteration_statement
| selection_statement
| labeled_statement
| expression_statement
iteration_statement:
'for' '(' <commit> for_initialization(?) for_expression(?) for_increment(?) ')'
statement[context => 'for loop']
{
my $initialization = join('', @{$item{'for_initialization(?)'}});
my $expression = join('',@{$item{'for_expression(?)'}});
my $increment = join('',@{$item{'for_increment(?)'}});
if ($initialization) {
$return .= "Prepare a loop by ^L$initialization, then ^L";
}
if (length $expression) {
if ($expression =~ /^(\d+)$/) {
if($expression == 0) {
$return .= "Never repeatedly ^L";
} else {
$return .= "Repeatedly ^L";
}
} else {
my $expression = ::istrue $expression;
$return .= "For as long as ^L$expression, ^L";
}
} else {
$return .= "Repeatedly ^L";
}
$return .= $item{statement};
if ($increment) {
$return =~ s/End for loop.$//;
$return .= "After each iteration, ^L$increment.\n";
}
}
| 'while' '(' <commit> expression[context => 'while conditional'] ')' statement[context => 'while loop']
{
if ($item{expression} =~ /(^\d+$)/) {
if ($1 == 0) {
$return = "Never repeatedly ^L";
} else {
$return = "Repeatedly ^L";
}
} else {
my $expression = ::istrue $item{expression};
$return = "While ^L$expression, ^L";
}
if ($item{statement}) {
$return .= $item{statement} . "\n";
} else {
$return .= "do nothing.\n";
}
}
| 'do' statement[context => 'do loop'] 'while' '(' expression[context => 'do while conditional'] ')' ';'
{
$item{statement} =~ s/^do nothing/nothing/i;
$return = "Do the following:^L $item{statement}";
if ($item{expression} =~ /(^\d+$)/) {
if ($1 == 0) {
$return .= "Do this once.\n";
} else {
$return .= "Do this repeatedly.\n";
}
} else {
my $expression = ::istrue $item{expression};
$return .= "Do this as long as ^L$expression.\n";
}
}
for_initialization:
declaration[context => 'for init']
| expression_statement[context => 'for init']
for_expression:
expression_statement[context => 'for conditional']
for_increment:
expression[context => 'for increment statement']
selection_statement:
'if' <commit> '(' expression[context => 'if conditional'] ')' statement[context => "$arg{context}|if statement"]
{
if ($item{expression} =~ /^(\d+)$/) {
if ($1 == 0) {
$return = "Never ";
} else {
$return = "Always ";
}
} else {
my $expression = ::istrue $item{expression};
$return = "If ^L$expression then ";
}
$return .= "^L$item{statement}";
}
('else' statement[context => "$arg{context}|else block"]
{ "Otherwise, ^L$item{statement}" }
)(?)
{ $return .= join('',@{$item[-1]}); }
| 'switch' '(' expression[context => 'switch conditional'] ')' statement[context => "$arg{context}|switch"]
{ "When given the expression ^L$item{expression}, ^L$item{statement}" }
jump_statement:
'break' ';'
{
if ($arg{context} =~ /switch/ or $arg{context} =~ /case/) {
$return = "Exit switch block.\n";
} elsif (length $arg{context}) {
my ($context) = $arg{context} =~ /([^|]+)/;
$return = "Break from the $context.\n";
} else {
$return = "Break from the current block.\n";
}
}
| 'continue' ';'
{ "Return to the top of the current loop.\n" }
| 'return' <commit> expression[context => "$arg{context}|return expression"](?) ';'
{
my $expression = join('', @{$item{'expression(?)'}});
if (length $expression) {
$return = "Return ^L$expression.\n";
} else {
$return = "Return no value.\n";
}
}
| 'goto' <commit> identifier ';' comment(?)
{
$return = "Go to the label named $item{identifier}.\n";
$return .= join('', @{$item{'comment(?)'}});
}
expression_statement:
expression[context => "$arg{context}|statement"](?) ';'
{
my $expression = join('',@{$item[1]});
if (not length $expression) {
if ($arg{context} eq 'label'
or $arg{context} eq 'for init'
or $arg{context} eq 'for conditional') {
$return = "";
} else {
$return = "Do nothing.\n";
}
} else {
$return = $expression;
$return .= ".\n" unless $arg{context} =~ /for (init|conditional)$/;
}
}
labeled_statement:
identifier ':' statement[context => 'label'] (';')(?)
{ "Let there be a label $item{identifier}.\n$item{statement}" }
| ('case' constant_expression
{ $item{constant_expression} }
':')(s)
{
my @items = @{$item[1]};
if (@items <= 2) {
$return = join(' or ', @{$item[1]});
} else {
my $last = pop @items;
$return = join(', ', @items) . " or $last";
}
}
(statement[context => "$arg{context}|case"])(s)
{
my $last = pop @{$item[-1]};
my $statements = join('', @{$item[-1]});
if (length $statements and $statements !~ /Exit switch block\.\s*$/) {
$statements .= "Fall through to the next case.\n";
} elsif (not length $statements and not $last) {
$statements = "Do nothing.\n";
}
$return = "If it has the value $item[-2], ^L$statements$last";
}
| 'default' ':' statement
{ "In the default case, ^L$item{statement}" }
expression:
<leftop: assignment_expression ',' assignment_expression>
{
if ($arg{context} eq 'for increment statement'
or $arg{context} eq 'for init') {
$return = join(', then ', @{$item[-1]});
} elsif ( $arg{context} =~ /conditional/) {
$return = join(' and the result discarded and ', @{$item[-1]});
} else {
$return .= "Evaluate " if @{$item[-1]} > 1;
$return .= join(" and discard the result and then evaluate ^L", @{$item[-1]});
}
}
assignment_expression:
unary_expression[context => 'assignment expression']
assignment_operator
assignment_expression[context => 'assignment expression']
{
my $assignment_expression = $item{assignment_expression};
my $assignment_operator = $item{assignment_operator};
if (ref $assignment_operator eq 'ARRAY') {
$return .= "${$item{assignment_operator}}[0] $item{unary_expression} ";
${$item{assignment_operator}}[1] =~ s/the value/the result of the expression/ if $assignment_expression =~ / /;
$return .= "${$item{assignment_operator}}[1] " if $assignment_expression !~ /the result of/;
$return .= $assignment_expression;
} else {
$return = "$item{unary_expression} $assignment_operator $assignment_expression";
}
}
| conditional_expression
conditional_expression:
logical_OR_AND_expression conditional_ternary_expression
{
if ($item{conditional_ternary_expression}) {
my $op1 = $item{conditional_ternary_expression}->[0];
my $op2 = $item{conditional_ternary_expression}->[1];
my $expression = ::istrue $item{logical_OR_AND_expression};
if ($arg{context} =~ /initializer expression$/) {
$return = "$op1 if $expression otherwise to $op2";
} elsif ($arg{context} =~ /assignment expression$/) {
$return = "$op1 if $expression otherwise the value $op2";
} else {
$return = "$op1 if $expression otherwise $op2";
}
} else {
$return = $item{logical_OR_AND_expression};
}
}
conditional_ternary_expression:
'?' expression ':' conditional_expression
{ [$item{expression}, $item{conditional_expression}] }
| {""}
assignment_operator:
'='
{
if ($arg{context} =~ /for init/) {
['assigning to^L', 'the value^L' ]
} elsif ($arg{context} =~ /statement$/) {
['Assign to^L', 'the value^L' ]
} else {
'which is assigned to be^L'
}
}
| '+='
{
if ($arg{context} =~ /for init/) {
['incrementing^L','by^L']
} elsif ($arg{context} =~ /statement$/) {
['Increment^L','by^L']
} else {
'which is incremented by^L'
}
}
| '-='
{
if ($arg{context} =~ /for init/) {
['decrementing^L' , 'by^L']
} elsif ($arg{context} =~ /statement$/) {
['Decrement^L', 'by^L']
} else {
'which is decremented by^L'
}
}
| '*='
{
if ($arg{context} =~ /for init/) {
['multiplying^L' , 'by^L']
} elsif ($arg{context} =~ /statement$/) {
['Multiply^L' , 'by^L']
} else {
'which is multiplied by^L'
}
}
| '/='
{
if ($arg{context} =~ /for init/) {
['dividing^L' , 'by^L' ]
} elsif ($arg{context} =~ /statement$/) {
['Divide^L' , 'by^L' ]
} else {
'which is divided by^L'
}
}
| '%='
{
if ($arg{context} =~ /for init/) {
['reducing^L', 'to modulo ^L']
} elsif ($arg{context} =~ /statement$/) {
['Reduce^L', 'to modulo ^L']
} else {
'which is reduced to modulo^L'
}
}
| '<<='
{
if ($arg{context} =~ /for init/) {
['bit-shifting^L', 'left by^L']
} elsif ($arg{context} =~ /statement$/) {
['Bit-shift^L', 'left by^L']
} else {
'which is bit-shifted left by^L'
}
}
| '>>='
{
if ($arg{context} =~ /for init/) {
['bit-shifting^L', 'right by^L']
} elsif ($arg{context} =~ /statement$/) {
['Bit-shift^L', 'right by^L']
} else {
'which is bit-shifted right by^L'
}
}
| '&='
{
if ($arg{context} =~ /for init/) {
['bitwise-ANDing^L', 'by^L' ]
} elsif ($arg{context} =~ /statement$/) {
['Bitwise-AND^L', 'by^L' ]
} else {
'which is bitwise-ANDed by^L'
}
}
| '^='
{
if ($arg{context} =~ /for init/) {
['exclusive-ORing^L','by^L']
} elsif ($arg{context} =~ /statement$/) {
['Exclusive-OR^L','by^L']
} else {
'which is exclusive-ORed by^L'
}
}
| '|='
{
if ($arg{context} =~ /for init/) {
['bitwise-ORing^L', 'by^L']
} elsif ($arg{context} =~ /statement$/) {
['Bitwise-OR^L', 'by^L']
} else {
'which is bitwise-ORed by^L'
}
}
constant_expression:
conditional_expression
logical_OR_AND_expression:
<leftop:
rel_add_mul_shift_expression
log_OR_AND_bit_or_and_eq
rel_add_mul_shift_expression[context => 'logical_OR_AND_expression']>
{
my $expression = join('', @{$item[1]});
if($arg{context} =~ /initializer expression$/
and $expression =~ / /
and $expression !~ /^the .*? constant \S+$/i
and $expression !~ /the size of/i
and $expression !~ /the offset/i
and $expression !~ /the address of/i
and $expression !~ /^the result of the/) {
$return = 'the result of the expression ^L';
}
$return .= $expression;
}
log_OR_AND_bit_or_and_eq:
'||' { ' or ^L' }
| '&&' { ' and ^L' }
| '|' { ' bitwise-ORed by ^L' }
| '&' { ' bitwise-ANDed by ^L' }
| '^' { ' bitwise-XORed by ^L'}
| '==' { ' is equal to ^L' }
| '!=' { ' is not equal to ^L' }
rel_mul_add_ex_op:
'+' { ' plus ^L' }
| '-' { ' minus ^L' }
| '*' { ' times ^L' }
| '/' { ' divided by ^L' }
| '%' { ' modulo ^L' }
| '<<' { ' shifted left by ^L' }
| '>>' { ' shifted right by ^L' }
| '>=' { ' is greater than or equal to ^L' }
| "<=" { ' is less than or equal to ^L' }
| '>' { ' is greater than ^L' }
| '<' { ' is less than ^L' }
unary_operator:
'&' { 'the address of ^L' }
| '*' { 'the dereference of ^L' }
| '+' { '' }
| '-' ...identifier { 'negative ^L' }
| '-' { 'minus ^L' }
| '~' { "the one's complement of ^L" }
| '!' '!' { 'the normalized boolean value of ^L' }
| '!'
{
if ($arg{context} =~ /conditional/) {
['', ' is false']
} else {
'the logical negation of ^L'
}
}
rel_add_mul_shift_expression:
cast_expression ...';'
{ $item{cast_expression} }
| <leftop: cast_expression rel_mul_add_ex_op cast_expression>
{ join('', @{$item[1]}) }
closure:
',' | ';' | ')'
cast_expression:
'(' type_name ')' cast_expression[context => 'recast']
{ "$item{cast_expression} converted to $item{type_name}" }
| unary_expression
{ $item{unary_expression} }
Static_assert:
'_Static_assert'
| 'static_assert'
static_assert_declaration:
Static_assert '(' constant_expression[context => 'static assert'] ',' string ')' ';'
{
my $expression = ::istrue $item{constant_expression};
"Halt compilation and produce the diagnostic $item{string} unless $expression.\n"
}
declaration_list:
preproc[context => 'statement'](?) declaration(s)
{ join('', @{$item{'preproc(?)'}}) . join('', @{$item{'declaration(s)'}}) }
declaration:
declaration_specifiers init_declarator_list(?) ';'
{
my @init_list = defined $item{'init_declarator_list(?)'}->[0] ? @{$item{'init_declarator_list(?)'}->[0]} : ('');
my $typedef = $item{declaration_specifiers} =~ s/^type definition of //;
my $noreturn = $item{declaration_specifiers} =~ s/_Noreturn//g;
$item{declaration_specifiers} = join(' ', split(' ', $item{declaration_specifiers}));
if ($noreturn) {
if($item{declaration_specifiers} eq 'void') {
$item{declaration_specifiers} = "which doesn't return to its caller";
} else {
$item{declaration_specifiers} = "which shouldn't return to its caller yet does mysteriously return $item{declaration_specifiers}";
}
}
my $inits = 0;
while (@init_list) {
$inits++;
if (not $arg{context} eq 'struct member') {
if ($arg{context} eq 'for init') {
$return .= "declaring ";
} else {
$return .= "Declare ";
}
}
my @args = ::flatten shift @init_list;
my ($first_qualifier, $first_initializer);
my $first_identifier = shift @args;
my @identifiers = ($first_identifier) unless not length $first_identifier;
foreach my $arg (@args) {
if ($arg =~ /initialized/) {
$first_initializer .= (length $first_initializer ? ' ' : '') . $arg;
} else {
$first_qualifier .= (length $first_qualifier ? ' ' : '') . $arg;
}
}
my @initializers;
if ($first_initializer) {
push @initializers, [ $first_identifier, $first_initializer ];
}
for (my $i = 0; $i < @init_list; $i++) {
@args = ::flatten $init_list[$i];
my ($qualifier, $initializer);
my $identifier = shift @args;
foreach my $arg (@args) {
if ($arg =~ /initialized/) {
$initializer .= (length $initializer ? ' ' : '') . $arg;
} else {
$qualifier .= (length $qualifier ? ' ' : '') . $arg;
}
}
next unless $qualifier eq $first_qualifier;
push @identifiers, $identifier;
if ($initializer) {
push @initializers, [ $identifier, $initializer ];
}
splice @init_list, $i--, 1;
}
if ($arg{context} eq 'struct member') {
if ($inits > 1 and not @init_list) {
$return .= ' and ';
} elsif ($inits > 1) {
$return .= ', ';
}
my $and = @identifiers > 1 ? ' and ' : '';
my $comma = '';
for (my $i = 0; $i < @identifiers; $i++) {
if ($i == @identifiers - 1) {
$return .= "$and$identifiers[$i]";
} else {
$return .= "$comma$identifiers[$i]";
$comma = ', ';
}
}
$return .= ' as ' unless not @identifiers;
if ($first_qualifier) {
if ($first_qualifier =~ /bit\-field/) {
$first_qualifier = "$item{declaration_specifiers} $first_qualifier";
$item{declaration_specifiers} = '';
}
if (@identifiers == 1 and $first_qualifier !~ /^(an|a)\s+/) {
$return .= $first_qualifier =~ m/^[aeiou]/ ? 'an ' : 'a ';
} elsif (@identifiers > 1 and not $typedef) {
$first_qualifier =~ s/pointer/pointers/;
$first_qualifier =~ s/an array/arrays/;
}
$return .= "$first_qualifier";
$return .= " $item{declaration_specifiers}" if $item{declaration_specifiers};
} else {
if (@identifiers == 1 and $item{declaration_specifiers} !~ /^(an|a)\s+/) {
$return .= $item{declaration_specifiers} =~ m/^[aeiou]/ ? 'an ' : 'a ';
}
$return .= $item{declaration_specifiers};
}
} else {
my $and = @identifiers > 1 ? ' and ' : '';
my $comma = '';
for (my $i = 0; $i < @identifiers; $i++) {
if ($i == @identifiers - 1) {
$return .= "$and$identifiers[$i]";
} else {
$return .= "$comma$identifiers[$i]";
$comma = ', ';
}
}
if ($typedef) {
$return .= ' each' if @identifiers > 1;
$return .= ' as another name for ';
push @typedefs, @identifiers;
} else {
$return .= ' as ' unless not @identifiers;
}
if ($first_qualifier) {
if ($noreturn) {
$first_qualifier =~ s/ returning$//;
}
if (@identifiers == 1 and $first_qualifier !~ /^(an|a)\s+/) {
$return .= $first_qualifier =~ m/^[aeiou]/ ? 'an ' : 'a ';
} elsif (@identifiers > 1 and not $typedef) {
$first_qualifier =~ s/pointer/pointers/;
$first_qualifier =~ s/an array/arrays/;
}
$return .= "$first_qualifier ";
$return .= $item{declaration_specifiers};
} else {
if (@identifiers == 1 and $item{declaration_specifiers} !~ /^(an|a)\s+/) {
$return .= $item{declaration_specifiers} =~ m/^[aeiou]/ ? 'an ' : 'a ';
}
$return .= $item{declaration_specifiers};
}
if (@initializers) {
if (@identifiers > 1) {
$return .= ".\nInitialize ";
@initializers = sort { $a->[1] cmp $b->[1] } @initializers;
my ($and, $comma);
for (my $i = 0; $i < @initializers; $i++) {
my ($identifier, $initializer) = @{$initializers[$i]};
if ($i < @initializers - 1 and $initializer eq $initializers[$i + 1]->[1]) {
$return .= "$comma$identifier";
$comma = ', ';
$and = ' and ';
} else {
$initializer =~ s/^initialized to \^L//;
$return .= "$and$identifier to $initializer";
if ($i < @initializers - 2) {
$and = $comma = ', ';
} else {
$and = ' and ';
}
}
}
} else {
$return .= " $initializers[0]->[1]";
}
}
$return =~ s/,$//;
$return .= ".\n" unless $arg{context} eq 'for init';
}
}
}
| static_assert_declaration
init_declarator_list:
<leftop: init_declarator ',' init_declarator>
init_declarator:
declarator[context => "$arg{context}|init_declarator"]
{ $return = $item{declarator} }
('=' initializer)(?)
{
my $init = join('',@{$item[-1]});
if (length $init) {
$return = [$item{declarator}, "initialized to ^L$init"];
}
}
initializer:
designation initializer
{ "$item[1] $item[2]" }
| comment(?) assignment_expression[context => "$arg{context}|initializer expression"] comment(?)
{
$return = $item[2];
if (join('',@{$item[1]})) {
$return = '['.join('',@{$item[1]}).']' . $return;
}
if (join('',@{$item[1]})) {
$return .= join('',@{$item[-1]});
}
}
| '{' comment(?) initializer_list (',' )(?) '}'
{ '{' . $item{'initializer_list'} . '}' }
initializer_list:
<leftop: initializer ',' initializer >
{ join(', ', @{$item[1]}) }
designation:
designator_list '='
{ $item{designator_list} }
designator_list:
designator(s)
{
$return = join(' of ', reverse @{$item{'designator(s)'}});
$return .= ' set to';
}
designator:
'[' constant_expression ']'
{
my $expression = $item{constant_expression};
if ($expression =~ /^\d+$/) {
$expression++;
my ($last_digit) = $expression =~ /(\d)$/;
if ($last_digit == 1) {
if ($expression =~ /11$/) {
$expression .= 'th';
} else {
$expression .= 'st';
}
} elsif ($last_digit == 2) {
$expression .= 'nd';
} elsif ($last_digit == 3) {
$expression .= 'rd';
} else {
$expression .= 'th';
}
$expression = "the $expression element";
} else {
$expression = "the element at location ^L$expression^L";
}
$return = $expression;
}
| '.' identifier
{ "the member $item{identifier}" }
unary_expression:
postfix_expression
| '++' unary_expression
{
if ($arg{context} =~ /for init/) {
$return = "pre-incrementing $item{unary_expression}";
} elsif ($arg{context} =~ /(conditional|expression)/) {
if ($item{unary_expression} =~ s/^the member//) {
$return = "the pre-incremented member $item{unary_expression}";
} elsif ($item{unary_expression} =~ s/^the element//) {
$return = "the pre-incremented element $item{unary_expression}";
}else {
$return = "pre-incremented $item{unary_expression}";
}
} else {
$return = "pre-increment $item{unary_expression}";
}
}
| '--' unary_expression
{
if ($arg{context} =~ /for init/) {
$return = "pre-decrementing $item{unary_expression}";
} elsif ($arg{context} =~ /(conditional|expression)/) {
if ($item{unary_expression} =~ s/^the member//) {
$return = "the pre-decremented member $item{unary_expression}";
} elsif ($item{unary_expression} =~ s/^the element//) {
$return = "the pre-decremented element $item{unary_expression}";
} else {
$return = "pre-decremented $item{unary_expression}";
}
} else {
$return = "Pre-decrement $item{unary_expression}";
}
}
| unary_operator cast_expression
{
if (ref $item{unary_operator} eq 'ARRAY') {
$return = $item{unary_operator}->[0] . $item{cast_expression} . $item{unary_operator}->[1];
} else {
$return = $item{unary_operator} . $item{cast_expression};
}
}
| 'sizeof' unary_expression[context => 'sizeof']
{
if ($arg{context} =~ /statement$/) {
$return = "Evaluate and discard the size of ^L$item{unary_expression}";
} else {
$return = "the size of ^L$item{unary_expression}";
}
}
| 'sizeof' '(' type_name[context => 'sizeof'] ')'
{
if ($arg{context} =~ /statement$/) {
$return = "Evaluate and discard the size of the type $item{type_name}";
} else {
$return = "the size of the type $item{type_name}";
}
}
| 'sizeof' '(' assignment_expression[context => 'sizeof'] ')'
{
if ($arg{context} =~ /statement$/) {
$return = "Evaluate and discard the size of the type of the expression (^L$item{assignment_expression})";
} else {
$return = "the size of the type of the expression (^L$item{assignment_expression})";
}
}
| Alignof '(' type_name ')'
{ "the alignment of the type $item{type_name}" }
| 'offsetof' '(' type_name[context => 'offsetof'] ',' identifier ')'
{ "the offset, in bytes, of member $item{identifier} from the beginning of $item{type_name}" }
Alignof:
'_Alignof'
| 'alignof'
postfix_productions:
'(' argument_expression_list(?) ')' postfix_productions[context => 'function call'](?)
{
my $postfix = $item[-1]->[0];
$arg{primary_expression} =~ s/^Evaluate the expression/resulting from the expression/;
if($arg{context} =~ /statement$/) {
$return = "Call the function $arg{primary_expression}";
} else {
$return = "the result of the function $arg{primary_expression}";
}
# To discriminate between macros and functions.
foreach (@macros) {
if ($arg{primary_expression} eq $_) {
$return =~ s/Call/Insert/;
$return =~ s/function/macro/;
}
}
my $arg_exp_list = join('',@{$item{'argument_expression_list(?)'}});
if (length $arg_exp_list) {
$return .= " with argument$arg_exp_list";
}
if ($postfix) {
$return =~ s/^(Call|Insert)/the result of/;
$return = "$postfix $return";
}
1;
}
| '[' expression[context => 'array address'] ']' postfix_productions[context => "$arg{context}|array address"](?)
{
my $expression = $item[2];
my $postfix = $item[-1]->[0];
if (length $expression) {
if ($expression =~ /^\d+$/) {
$expression++;
my ($last_digit) = $expression =~ /(\d)$/;
if ($last_digit == 1) {
if ($expression =~ /11$/) {
$expression .= 'th';
} else {
$expression .= 'st';
}
} elsif ($last_digit == 2) {
$expression .= 'nd';
} elsif ($last_digit == 3) {
$expression .= 'rd';
} else {
$expression .= 'th';
}
if ($arg{context} eq 'function call') {
$return = "the $expression element of^L";
} else {
$return = "the $expression element of^L";
$return .= " $arg{primary_expression}" if $arg{primary_expression};
}
} elsif ($expression =~ /^-\s*\d+$/) {
$expression *= -1;
my $plural = $expression == 1 ? '' : 's';
$return = "the element $expression element$plural backwards from where ^L$arg{primary_expression} points^L";
} else {
$return = "the element at location ^L$expression of^L";
$return .= " $arg{primary_expression}" if $arg{primary_expression};
}
}
if ($postfix) {
$return = "$postfix $return";
$return =~ s/the post-([^ ]+) the/the post-$1/g;
}
}
| '.' identifier postfix_productions[context => "$arg{context}|struct access"](?)
{
my $identifier = $item[-2];
my $postfix = $item[-1]->[0];
if ($postfix) {
if (ref $postfix eq 'ARRAY') {
$return = "$postfix->[0] the member $identifier $postfix->[1] of";
} else {
if ($arg{context} =~ /conditional/ or $arg{context} =~ /assignment expression/) {
$return = "$postfix member $identifier of";
$return .= " the" unless $arg{context} =~ /array address/;
} else {
$postfix =~ s/ the(\^L)?$/$1/;
$return = "$postfix the member $identifier of";
$return .= " the" unless $arg{context} =~ /array address/;
}
if ($arg{primary_expression}) {
$return =~ s/ the(\^L)?$/$1/;
$return .= " ^L$arg{primary_expression}"
}
}
} else {
if ($arg{context} =~ /array address/) {
$return = "the member $identifier of^L";
} else {
$return = "the member $identifier of the^L";
if ($arg{primary_expression}) {
$return =~ s/ the(\^L)?$/$1/;
$return .= " $arg{primary_expression}";
}
}
}
1;
}
| '->' identifier postfix_productions[context => "$arg{context}|struct access"](?)
{
my $identifier = $item[-2];
my $postfix = $item[-1]->[0];
if ($postfix) {
if (ref $postfix eq 'ARRAY') {
$return = "$postfix->[0] the member $identifier $postfix->[1] of the structure pointed to by^L";
} else {
if ($arg{context} =~ /conditional/ or $arg{context} =~ /assignment expression/) {
$return = "$postfix member $identifier of the structure pointed to by the^L";
} else {
$postfix =~ s/ the(\^L)?$/$1/;
$return = "$postfix the member $identifier of the structure pointed to by the^L";
}
}
} else {
$return = "the member $identifier of the structure pointed to by the^L";
}
if ($arg{primary_expression}) {
$return =~ s/ the(\^L)?$/$1/;
$return .= " $arg{primary_expression}";
}
1;
}
| ('++')(s)
{
my $increment = join('',@{$item[-1]});
if ($increment) {
if ($arg{context} =~ /(struct access|array address)/) {
if ($arg{context} =~ /conditional/ or $arg{context} =~ /assignment expression/) {
$return = "the post-incremented";
} else {
$return = "post-increment";
}
} elsif ($arg{context} =~ /for init/) {
$return = ['incrementing', 'by one'];
} elsif ($arg{context} =~ /(conditional|expression)/) {
$return = "post-incremented $arg{primary_expression}";
} else {
$return = ['increment', 'by one'];
}
}
}
| ('--')(s)
{
my $increment = join('',@{$item[-1]});
if ($increment) {
if ($arg{context} =~ /(struct access|array address)/) {
if ($arg{context} =~ /conditional/ or $arg{context} =~ /assignment expression/) {
$return = "the post-decremented";
} else {
$return = "post-decrement";
}
} elsif ($arg{context} =~ /for init/) {
$return = ['decrementing', 'by one'];
} elsif ($arg{context} =~ /(conditional|expression)/) {
$return = "post-decremented $arg{primary_expression}";
} else {
$return = ['decrement', 'by one'];
}
}
}
| {""}
postfix_expression:
'(' type_name ')' '{' initializer_list '}' postfix_productions[context => "$arg{context}|compound literal"](?)
{
my $postfix = $item[-1]->[0];
$return = "A compound literal of type $item{type_name} initialized to {$item{initializer_list}}";
$return = "$postfix $return" if $postfix;
}
| primary_expression postfix_productions[primary_expression => $item[1], context => $arg{context}]
{
my $postfix_productions = $item{'postfix_productions'};
if (ref $postfix_productions eq 'ARRAY') {
$return = "$postfix_productions->[0] $item{primary_expression} $postfix_productions->[1]";
} elsif (length $postfix_productions) {
$return = $postfix_productions;
} elsif (length $item{primary_expression}) {
$return = $item{primary_expression};
} else {
$return = undef;
}
}
postfix_suffix:
'[' expression ']'
| '.' identifier
| '->' identifier
| '++'
| '--'
argument_expression_list:
<leftop: assignment_expression[context => 'function argument'] ',' assignment_expression[context => 'function argument']>
{
my @arg_exp_list = @{$item[1]};
my $last = '';
if (@arg_exp_list > 2) {
$last = pop @arg_exp_list;
$return = 's ' . join(', ^L', @arg_exp_list) . " and ^L$last";
} elsif (@arg_exp_list == 2 ) {
$return = "s ^L$arg_exp_list[0] and ^L$arg_exp_list[1]";
} else {
if (length $arg_exp_list[0]) {
$return = " ^L$arg_exp_list[0]";
} else {
$return = '';
}
}
}
narrow_closure:
';' | ',' | '->'
primary_expression:
'(' expression[context => "$arg{context}|expression"] ')' (...narrow_closure)(?)
{
my $expression = $item{expression} ;
my $repeats = 1;
if ($expression =~ /^The expression (\(+)/) {
$repeats = (length $1) + 1;
$expression =~ s/^The expression \(+//;
}
$expression .= ')';
if ($arg{context} =~ /statement$/) {
$return = "Evaluate the expression ";
} else {
#$return = "The result of the expression ";
}
$return .= '(' x $repeats;
$return .= "^L$expression";
}
| constant
| string
| identifier
| generic_selection
| {} # nothing
generic_selection:
'_Generic' '(' assignment_expression ',' generic_assoc_list ')'
{ "a generic-selection on $item{assignment_expression} yielding $item{generic_assoc_list}" }
generic_assoc_list:
<leftop: generic_association ',' generic_association>
{
if (@{$item[-1]} == 1) {
$return = $item[-1]->[0];
} else {
my $last = pop @{$item[-1]};
$return = join(', ', @{$item[-1]}) . " and $last";
}
}
generic_association:
type_name ':' assignment_expression
{ "$item{assignment_expression} in the case that it has type $item{type_name}" }
| 'default' ':' assignment_expression
{ "$item{assignment_expression} in the default case" }
Alignas:
'_Alignas'
| 'alignas'
alignment_specifier:
Alignas '(' type_name ')'
{ "with alignment of the type $item{type_name}" }
| Alignas '(' constant_expression ')'
{
my $plural = $item{constant_expression} != 1 ? 's' : '';
"with alignment of $item{constant_expression} byte$plural between objects"
}
declarator:
direct_declarator(s)
{
my @direct_declarator = @{$item{'direct_declarator(s)'}};
if (@direct_declarator == 1) {
$return = $direct_declarator[0];
} else {
$return = $item{'direct_declarator(s)'};
}
}
| pointer direct_declarator(s)
{
push @{$item{'direct_declarator(s)'}}, $item{pointer};
$item{'direct_declarator(s)'}
}
direct_declarator:
identifier ':' constant
{
my $bits = $item{constant} == 1 ? "$item{constant} bit" : "$item{constant} bits";
[$item{identifier}, "bit-field of $bits"]
}
| identifier[context => 'direct_declarator'] array_declarator(s?)
{
if (@{$item{'array_declarator(s?)'}}) {
$return = [$item{identifier}, join(' ', @{$item{'array_declarator(s?)'}})];
} else {
$return = $item{identifier};
}
}
| '(' declarator ')' array_declarator(s)
{
push @{$item{declarator}}, join(' ', @{$item{'array_declarator(s)'}});
$item{declarator}
}
| '(' parameter_type_list ')'
{ "function taking $item{parameter_type_list} and returning" }
| '(' declarator array_declarator(s) ')'
{ $item{'declarator'} . join(' ', @{$item{'array_declarator(s)'}}) }
| '(' declarator ')'
{ $item{declarator} }
array_qualifiers:
type_qualifier_list array_qualifiers(?)
{
$return = $item{'type_qualifier_list'};
my $qualifiers = join('', @{$item{'array_qualifiers(?)'}});
$return .= " $qualifiers" if $qualifiers;
}
| 'static' array_qualifiers(?)
{
$return = $item[1];
my $qualifiers = join('', @{$item{'array_qualifiers(?)'}});
$return .= " $qualifiers" if $qualifiers;
}
array_declarator:
'[' array_qualifiers(?) assignment_expression(?) ']'
{
my $size;
if (@{$item{'assignment_expression(?)'}}) {
$size = join('', @{$item{'assignment_expression(?)'}});
if ($size =~ /^(unsigned|long)*\s*1$/) {
$size = "$size element";
} else {
$size = "$size elements";
}
} else {
if ($arg{context} =~ /struct member/) {
$size = 'flexible length';
} else {
$size = 'unspecified length';
}
}
my $qualifiers = join('', @{$item{'array_qualifiers(?)'}});
if ($qualifiers) {
if($qualifiers =~ s/static//g) {
$qualifiers = join(' ', split(' ', $qualifiers));
if($qualifiers) {
$return = "a $qualifiers array ";
} else {
$return = "an array ";
}
$return .= "with optimization hint to provide access to the first element of $size of";
} else {
$return = "an $qualifiers array of $size of";
}
} else {
$return = "an array of $size of";
}
}
| '[' '*' ']'
{ $return = 'an array of variable length of unspecified size of'; }
identifier_list:
(identifier ',')(s?) identifier
{
my @identifier_list = @{$item[1]};
if ($#identifier_list > 1) {
$return = join(', ', @identifier_list) . ' and ' . $item{identifier};
} elsif ($#identifier_list == 1) {
$return = $identifier_list[1] . ' and ' . $item{identifier};
} else {
$return = $item{identifier};
}
}
parameter_type_list:
parameter_list
parameter_list:
<leftop: parameter_declaration ',' parameter_declaration>
{
my @parameter_list = @{$item[1]};
my $comma = '';
for (my $i = 0; $i < @parameter_list; $i++) {
$return .= $comma;
if (ref $parameter_list[$i] eq 'ARRAY') {
my @list = ::flatten @{$parameter_list[$i]};
if (@list == 0) {
$return = "no arguments";
} elsif (@list == 1) {
if ($list[0] eq 'void') {
$return = "no arguments";
} else {
$return .= $list[0];
}
} else {
push @list, shift @list;
if ($list[0] =~ /^`.*`$/) {
my $identifier = shift @list;
$return .= "$identifier as ";
$return .= join(' ', @list);
} else {
$return .= join(' ', @list);
}
}
} else {
$return .= $parameter_list[$i];
}
if ($i == $#parameter_list - 1) {
$comma = ' and ';
} else {
$comma = ', ';
}
}
}
parameter_declaration:
declaration_specifiers declarator
{ [$item{declaration_specifiers}, $item{declarator}] }
| '...'
{ "variadic arguments" }
| declaration_specifiers abstract_declarator(?)
{ [$item{declaration_specifiers}, $item{'abstract_declarator(?)'}] }
| ''
{ "unspecified arguments" }
abstract_declarator:
pointer(?) direct_abstract_declarator(s)
{
my $pointer = join(' ', @{$item{'pointer(?)'}});
$return = "$pointer " if $pointer;
$return .= join(' ', @{$item{'direct_abstract_declarator(s)'}});
}
| pointer
direct_abstract_declarator:
'(' abstract_declarator ')'
{ $item{abstract_declarator} }
| '[' ']'
{ 'array of unspecified length of' }
| '[' '*' ']'
{ 'array of variable length of unspecified size of' }
| '[' array_qualifiers(?) assignment_expression(?) ']'
{
my $size;
if (@{$item{'assignment_expression(?)'}}) {
$size = join('', @{$item{'assignment_expression(?)'}});
if ($size =~ /^(unsigned|long)*\s*1$/) {
$size = "$size element";
} else {
$size = "$size elements";
}
} else {
$size = 'unspecified length';
}
my $qualifiers = join('', @{$item{'array_qualifiers(?)'}});
if ($qualifiers) {
if($qualifiers =~ s/static//g) {
$qualifiers = join(' ', split(' ', $qualifiers));
if($qualifiers) {
$return = "a $qualifiers array ";
} else {
$return = "an array ";
}
$return .= "with optimization hint to provide access to the first element of $size of";
} else {
$return = "an $qualifiers array of $size of";
}
} else {
$return = "an array of $size of";
}
}
| DAD '[' ']'
| DAD '[' array_qualifiers(?) assignment_expression(?) ']'
| '(' ')'
{ 'function taking unspecified arguments and returning' }
| '(' parameter_type_list ')'
{ "function taking $item{parameter_type_list} and returning" }
| DAD '(' ')'
| DAD '(' parameter_type_list ')'
DAD: # macro for direct_abstract_declarator
( '(' abstract_declarator ')' )(s?)
( '[' ']' )(s?)
( '[' assignment_expression ']' )(s?)
( '(' ')' )(s?)
( '(' parameter_type_list ')' )(s?)
identifier:
...!reserved identifier_word
{
if (not grep { $_ eq $item{identifier_word} } @identifiers) {
push @identifiers, $item{identifier_word};
}
$item{identifier_word}
}
pointer:
'*' type_qualifier_list(s) pointer(?)
{
$return = join('', @{$item{'pointer(?)'}}) if @{$item{'pointer(?)'}};
$return .= ' ' . join('', @{$item{'type_qualifier_list(s)'}}) . ' pointer to';
}
| '*' pointer(?)
{
my $pointers = join('', @{$item{'pointer(?)'}});
$return .= "$pointers " if $pointers;
$return .= 'pointer to';
}
type_qualifier_list:
type_qualifier(s)
{ join(' ', @{$item{'type_qualifier(s)'}}) }
function_specifier:
'inline'
| '_Noreturn'
| 'noreturn'
{ '_Noreturn' }
declaration_specifiers:
comment[context => 'declaration_specifiers'] declaration_specifiers(s)
{ "$item{comment} " . join(' ', @{$item{'declaration_specifiers(s)'}}) }
| type_specifier ...identifier
{ $item{type_specifier} }
| storage_class_specifier declaration_specifiers(?)
{
my $decl_spec = join(' ', @{$item{'declaration_specifiers(?)'}});
if ($item{storage_class_specifier} =~ m/^with/) {
if ($decl_spec) { $return .= "$decl_spec "; }
$return .= $item{storage_class_specifier};
} else {
$return .= $item{storage_class_specifier};
if ($decl_spec) { $return .= " $decl_spec"; }
}
}
| type_specifier(s) declaration_specifiers(?)
{
my $decl_spec = join(' ', @{$item{'declaration_specifiers(?)'}});
if ($decl_spec =~ s/\s*(with.*)$//) {
push @{$item{'type_specifier(s)'}}, $1;
}
$return .= "$decl_spec " if $decl_spec;
$return .= join(' ', @{$item{'type_specifier(s)'}});
}
| type_qualifier declaration_specifiers(?)
{
my $decl_spec = join(' ',@{$item{'declaration_specifiers(?)'}});
$return = $item{type_qualifier};
$return .= " $decl_spec" if $decl_spec;
}
| function_specifier declaration_specifiers(?)
{
my $decl_spec = join(' ',@{$item{'declaration_specifiers(?)'}});
$return = $item{function_specifier};
$return .= " $decl_spec" if $decl_spec;
}
| alignment_specifier(s) declaration_specifiers(?)
{
my $decl_spec = join(' ',@{$item{'declaration_specifiers(?)'}});
if ($decl_spec) {
$return = "$decl_spec ";
$return .= 'or ' if $decl_spec =~ /with alignment/;
}
$return .= join(' or ', @{$item{'alignment_specifier(s)'}});
$return .= ', whichever is more strict' if $return =~ /or with alignment/;
}
storage_class_specifier:
'auto'
{ 'with automatic storage-duration' }
| 'extern'
{
if ($arg{context} eq 'function definition') {
'with external linkage'
} else {
'with external linkage, possibly defined elsewhere'
}
}
| 'static'
{
if ($arg{context} eq 'function definition') {
'with internal linkage'
} elsif ($arg{context} eq 'function definition statement') {
'with life-time duration'
} else {
'with internal linkage and life-time duration'
}
}
| 'register'
{ 'with a suggestion to be as fast as possible' }
| 'typedef'
{ 'type definition of' }
type_qualifier:
'const'
| 'volatile'
| 'restrict'
| '_Atomic'
{ 'atomic' }
atomic_type_specifier:
'_Atomic' '(' type_name ')'
{ "atomic $item{type_name}" }
type_specifier:
<skip:''> /\s*/
('void'
| 'signed' | 'unsigned'
| 'FILE' | 'fpos_t'
| 'bool' | '_Bool'
| '_Complex' | '_Imaginary'
| 'int_fast8_t' | 'int_fast16_t' | 'int_fast24_t' | 'int_fast32_t' | 'int_fast64_t' | 'int_fast128_t'
| 'uint_fast8_t' | 'uint_fast16_t' | 'uint_fast24_t' | 'uint_fast32_t' | 'uint_fast64_t' | 'uint_fast128_t'
| 'int_least8_t' | 'int_least16_t' | 'int_least24_t' | 'int_least32_t' | 'int_least64_t' | 'int_least128_t'
| 'uint_least8_t' | 'uint_least16_t' | 'uint_least24_t' | 'uint_least32_t' | 'uint_least64_t' | 'uint_least128_t'
| 'int8_t' | 'int16_t' | 'int24_t' | 'int32_t' | 'int64_t' | 'int128_t'
| 'uint8_t' | 'uint16_t' | 'uint24_t' | 'uint32_t' | 'uint64_t' | 'uint128_t'
| 'intmax_t' | 'uintmax_t'
| 'intptr_t' | 'uintptr_t' | 'ptrdiff_t'
| 'sig_atomic_t'
| 'wint_t' | 'wchar_t'
| 'size_t' | 'rsize_t' | 'max_align_t'
| 'mbstate_t' | 'char16_t' | 'char32_t'
| 'fenv_t' | 'fexcept_t'
| 'div_t' | 'ldiv_t' | 'lldiv_t' | 'imaxdiv_t'
| 'cnd_t' | 'thrd_t' | 'tss_t' | 'mtx_t' | 'tss_dtor_t' | 'thrd_start_t' | 'once_flag'
| 'clock_t' | 'time_t'
| <skip:'[\s]*'> struct_or_union_specifier
| <skip:'[\s]*'> enum_specifier
| <skip:'[\s]*'> atomic_type_specifier | typedef_name
| 'double' | 'float' | 'char' | 'short' | 'int' | 'long'
) .../\W/
{ $item[3] }
typedef_name:
identifier
{
my $answer = 0;
foreach (@typedefs) {
if ($item{identifier} eq $_) {
$answer = 1;
$return = ($item{identifier} =~ m/^`[aeiou]/ ? 'an ' : 'a ') . $item{identifier};
}
}
if (!$answer) { undef $answer; }
$answer;
}
struct_or_union_specifier:
comment(?) struct_or_union identifier(?) '{' struct_declaration_list '}'
{
my $identifier = join('',@{$item{'identifier(?)'}});
$return = join('',@{$item{'comment(?)'}}) . $item{struct_or_union};
if ($identifier) { $return .= " tagged $identifier"; }
my $plural = $item{struct_declaration_list} =~ / and (?!returning)/ ? 's' : '';
$return .= ", with member$plural $item{struct_declaration_list},";
}
| struct_or_union identifier
{
$item{struct_or_union} =~ s/^(an|a)\s+//;
$return = $item{identifier} =~ m/^`[aeiou]/ ? 'an' : 'a';
$return .= " $item{identifier} $item{struct_or_union}";
}
struct_declaration_list:
struct_declaration(s)
{
my $finaldec;
my @declarations = @{$item{'struct_declaration(s)'}};
if ($#declarations > 1) {
$finaldec = pop @declarations;
$return = join(', ', @declarations ) . ' and ' . $finaldec;
} elsif ($#declarations == 1) {
$return = join(' and ', @declarations);
} else {
$return = $declarations[0];
}
}
struct_declaration:
comment(s?) declaration[context => 'struct member'] comment(s?)
{ join('', @{$item[1]}) . $item{declaration} . join('', @{$item[-1]}) }
type_name:
specifier_qualifier_list abstract_declarator(?)
{
my $abstract_declarator = join('',@{$item{'abstract_declarator(?)'}});
$return = "$abstract_declarator " if $abstract_declarator;
$return .= $item{specifier_qualifier_list};
}
specifier_qualifier_list:
type_specifier specifier_qualifier_list(?)
{
$return = $item{type_specifier};
$return .= ' ' . join('', @{$item{'specifier_qualifier_list(?)'}}) if @{$item{'specifier_qualifier_list(?)'}};
}
| type_qualifier specifier_qualifier_list(?)
{
$return = $item{type_qualifier};
$return .= ' ' . join('', @{$item{'specifier_qualifier_list(?)'}}) if @{$item{'specifier_qualifier_list(?)'}};
}
struct_or_union:
comment(?) ('struct'
{ 'a structure' }
| 'union'
{ 'a union' }
) comment(?)
{
shift @item;
foreach (@item) {
if (ref($_)) {
$return .= join('',@{$_});
} else {
$return .= $_;
}
}
}
enum_specifier:
'enum' identifier(?) '{' enumerator_list '}'
{
$return .= 'an enumeration';
if (@{$item{'identifier(?)'}}){
$return .= ' of ' . join('',@{$item{'identifier(?)'}});
}
my @enumerator_list = @{$item{enumerator_list}};
if (@enumerator_list == 1) {
$return .= " comprising $enumerator_list[0]";
} else {
my $last = pop @enumerator_list;
$return .= ' comprising ' . join(', ', @enumerator_list) . " and $last";
}
}
| 'enum' identifier
{ "an enumeration of type $item{identifier}" }
enumerator_list:
<leftop:enumerator ',' enumerator>
enumerator:
identifier ( '=' constant_expression )(?)
{
$return = $item[1];
if (@{$item[-1]}) {
$return .= ' marking ^L' . join('', @{$item[-1]});
}
}
comment:
comment_c
| comment_cxx
comment_c:
m{/\*[^*]*\*+([^/*][^*]*\*+)*/}s
{
$return = $item[1];
$return =~ s|^/\*+\s*||;
$return =~ s|\s*\*+/$||;
$return =~ s/"/\\"/g;
if ($arg{context} =~ /statement/) {
$return = "\nA comment: \"$return\".\n";
} else {
$return = "(a comment: \"$return\")";
}
}
comment_cxx:
m{//(.*?)\n}
{
$return = $item[1];
$return =~ s|^//\s*||;
$return =~ s/\n*$//;
$return =~ s/"/\\"/g;
$return = "\nA quick comment: \"$return\".\n";
}
constant:
/-?[0-9]*\.[0-9]*[lf]{0,2}/i
{
if ($item[1] =~ s/f$//i) {
$return = "the floating point constant $item[1]";
} elsif ($item[1] =~ s/l$//i) {
$return = "long double $item[1]";
} else {
$return = $item[1];
}
$return .= '0' if $return =~ /\.$/;
}
| /0x[0-9a-f]+[lu]{0,3}/i
{
$return .= 'unsigned ' if $item[1] =~ s/[Uu]//;
$return .= 'long ' while $item[1] =~ s/[Ll]//;
$return = "the $return" . "hexadecimal constant $item[1]";
}
| /0\d+[lu]{0,3}/i
{
$return .= 'unsigned ' if $item[1] =~ s/[Uu]//;
$return .= 'long ' while $item[1] =~ s/[Ll]//;
$return = "the $return" . "octal constant $item[1]";
}
| /-?[0-9]+[lu]{0,3}/i # integer constant
{
$return .= "unsigned " if $item[-1] =~ s/[Uu]//;
$return .= "long " while $item[-1] =~ s/[Ll]//;
$return .= $item[-1];
}
| /[LuU]?(?:\'(?:\\\'|(?!\').)*\')/ # character constant
{
my $constant = $item[1];
my $modifier = "";
$modifier = 'wide character ' if $constant =~ s/^L//;
$modifier = '16-bit character ' if $constant =~ s/^u//;
$modifier = '32-bit character ' if $constant =~ s/^U//;
if ($constant eq q('\n')) {
"a $modifier" . 'newline';
} elsif ($constant eq q('\f')) {
"a $modifier" . 'form-feed character';
} elsif ($constant eq q('\t')) {
"a $modifier" . 'tab';
} elsif ($constant eq q('\v')) {
"a $modifier" . 'vertical tab';
} elsif ($constant eq q('\a')) {
if (not length $modifier) {
'an alert character'
} else {
"a $modifier" . 'alert character'
}
} elsif ($constant eq q('\r')) {
"a $modifier" . 'carriage-return';
} elsif ($constant eq q('\b')) {
"a $modifier" . 'backspace character';
} elsif ($constant eq q('\'')) {
"a $modifier" . 'single-quote';
} elsif ($constant eq q(' ')) {
"a $modifier" . 'space';
} else {
if (not length $modifier) {
$constant
} else {
"a $modifier$constant"
}
}
}
identifier_word:
/[a-z_\$][a-z0-9_]*/i
{ "`$item[1]`" }
string:
(/(u8|u|U|L)?(?:\"(?:\\\"|(?!\").)*\")/)(s)
{
my $final_string = "";
foreach my $string (@{$item[-1]}) {
my $modifier = "";
$modifier = 'an UTF-8 string ' if $string =~ s/^u8//;
$modifier = 'a wide character string ' if $string =~ s/^L//;
$modifier = 'a 16-bit character string ' if $string =~ s/^u//;
$modifier = 'a 32-bit character string ' if $string =~ s/^U//;
if (not length $final_string) {
$final_string = $modifier;
$final_string .= '"';
}
$string =~ s/^"//;
$string =~ s/"$//;
$final_string .= $string;
}
$final_string .= '"';
$return = $final_string;
}
reserved:
/(auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto
|if|inline|int|long|register|restrict|return|short|signed|sizeof|static|struct|switch|typedef
|union|unsigned|void|volatile|while|_Alignas|alignas|_Alignof|alignof|_Atomic|_Bool|_Complex|_Generic
|_Imaginary|_Noreturn|noreturn|_Static_assert|static_assert|_Thread_local|offsetof)\b/x