Compare commits

...
7 Commits
Author SHA1 Message Date
Valentin Tolmer c8c9212bbd news: new syntax (%gprec and %precr) 2013-08-01 15:56:00 +02:00
Valentin Tolmer d992a222af regen 2013-08-01 15:56:00 +02:00
Valentin Tolmer 86a2a43854 tests: new tests for %gprec and %precr
3 grammars: one typical, with normal test cases, one to display all the
warnings introduced, and one with the errors introduced.

* tests/conflicts.at: New
2013-08-01 15:24:52 +02:00
Valentin Tolmer d8de391c38 syntax: introducing %precr to add specific precedence relations
It is now possible to add specific precedence relations between two symbols,
or a symbol and a group, with the %precr keyword:

%gprec arith {
  %left '+' '-'
  %left '*' '/'
}
%gprec boolean {
  %left OR
  %left AND
}
%right '^'

%precr '^' > arith
%precr OR AND > '^'

Here, the symbol ^ is of higher priority than the the ones in arith, but of
lower priority than both OR and AND. OR and '+', for example, cannot be
compared.

* src/parse-gram.y, src/scan-gram.l: Lexer and grammar implementation of
%precr.
* src/symtab.c, src/symtab.h: implementation of the addition of single link
precedence relationships.
2013-08-01 15:24:52 +02:00
Valentin Tolmer f8a710c6f4 syntax: introducing %gprec for precedence groups
It is now possible to introduce precedence groups, with precedence
relationships inside the group, but not with the outside tokens.  Ex:

%gprec arith {
  %left '+' '-'
  %left '*' '/'
  %right '^'
}

%gprec {
  %left OR
  %left AND
}

%left OTHER
%precedence OTHER2

Here, the arithmetical operators (in the "arith" group) can be compared, the
boolean operators can be compared, but OTHER can only be compared to OTHER2.

* src/gram.c, src/gram.h, src/scan-gram.l, src/parse-gram.y: {} blocks after
%gprec are understood by the lexer
* src/parse-gram.y: New syntax
* tests/input.at, tests/regression.at: Fix due to lexer change
2013-08-01 15:24:52 +02:00
Valentin Tolmer 2d8fc07778 conflicts: switch to partial order precedence system
Even though it is not yet fully deployed, this commit lays the ground for
the partial order precedence system, by changing to a graph-based order and
introducing precedence groups (only the default one can be used for now).

* src/symtab.h (struct symbol): Removed extra fields
* src/symtab.h: New function declarations
* src/symtab.c: New functions for precedence and groups, new hash table for
groups
* src/AnnotationList.c, src/conflicts.c, src/gram.c, src/print-xml.c,
* src/symtab.c: Adaptation to the new prec_node structure
* tests/existing.at (GAWK LALR): Fix
2013-08-01 15:24:52 +02:00
Valentin Tolmer a910d26cfb introduction of the new structures to prepare for partial order precedence
New structures: symgroup, prec_link, prec_node, and an enum of the
precedence relation operators.  Symbols have two more fields to prepare for
the precedence graph and grouping to come.

* src/symtab.h (struct symbol): Two new fields
* src/symtab.h: New structures
2013-08-01 15:24:52 +02:00
16 changed files with 1802 additions and 792 deletions
+26
View File
@@ -2,6 +2,32 @@ GNU Bison NEWS
* Noteworthy changes in release ?.? (????-??-??) [?]
** New syntax: partial-order precedence relationships
Formerly, the precedence order of tokens was linear, depending only on the
order in which they were declared. With the new syntax, all the tokens are
not necessarily comparable. It is possible to declare a group of tokens with
no links outside of the group, and to later on add only those needed.
The uncomparability of tokens would allow for more feedback on new conflicts
silently resolved via precedence.
An example of the new syntax applied to arithmetic and boolean operators,
with '^' serving as both numerical power and boolean XOR:
%gprec arith {
%left '+' '-'
%left '*' '/'
}
%gprec bool {
%left OR
%left AND
}
%gprec { %right '^' }
%precr '^' > arith
%precr OR AND > '^'
Here, AND is not comparable with '+', but '^' > '+' and AND > '^'
* Noteworthy changes in release 3.0 (2013-07-25) [stable]
+2 -2
View File
@@ -740,7 +740,7 @@ AnnotationList__computeDominantContribution (AnnotationList const *self,
if (reduce_precedence
&& (reduce_precedence < shift_precedence
|| (reduce_precedence == shift_precedence
&& token->content->assoc == right_assoc)))
&& token->content->prec_node->assoc == right_assoc)))
continue;
if (!AnnotationList__stateMakesContribution (self, nitems, ci,
lookaheads))
@@ -748,7 +748,7 @@ AnnotationList__computeDominantContribution (AnnotationList const *self,
/* This uneliminated reduction contributes, so see if it can cause
an error action. */
if (reduce_precedence == shift_precedence
&& token->content->assoc == non_assoc)
&& token->content->prec_node->assoc == non_assoc)
{
/* It's not possible to find split-stable domination over
shift after a potential %nonassoc. */
+77 -56
View File
@@ -53,7 +53,8 @@ enum conflict_resolution
reduce_resolution,
left_resolution,
right_resolution,
nonassoc_resolution
nonassoc_resolution,
uncomparable_resolution
};
@@ -90,6 +91,7 @@ log_resolution (rule *r, symbol_number token,
break;
case nonassoc_resolution:
case uncomparable_resolution:
obstack_printf (&solved_conflicts_obstack,
_(" Conflict between rule %d and token %s"
" resolved as an error"),
@@ -132,6 +134,12 @@ log_resolution (rule *r, symbol_number token,
" (%%nonassoc %s)",
symbols[token]->tag);
break;
case uncomparable_resolution:
obstack_printf (&solved_conflicts_obstack,
" (%s uncomparable with %s)",
r->prec->symbol->tag,
symbols[token]->tag);
break;
}
obstack_sgrow (&solved_conflicts_obstack, ".\n");
@@ -161,6 +169,7 @@ log_resolution (rule *r, symbol_number token,
xml_escape (symbols[token]->tag));
break;
case uncomparable_resolution:
case nonassoc_resolution:
obstack_printf (&solved_conflicts_xml_obstack,
" <resolution rule=\"%d\" symbol=\"%s\""
@@ -203,7 +212,13 @@ log_resolution (rule *r, symbol_number token,
obstack_printf (&solved_conflicts_xml_obstack,
"%%nonassoc %s",
xml_escape (symbols[token]->tag));
break;
break;
case uncomparable_resolution:
obstack_printf (&solved_conflicts_xml_obstack,
"%s uncomparable with %s",
xml_escape_n (0, symbols[token]->tag),
xml_escape_n (1, r->prec->symbol->tag));
break;
}
obstack_sgrow (&solved_conflicts_xml_obstack, "</resolution>\n");
@@ -243,7 +258,6 @@ flush_reduce (bitset lookahead_tokens, int token)
bitset_reset (lookahead_tokens, token);
}
/*------------------------------------------------------------------.
| Attempt to resolve shift-reduce conflict for one rule by means of |
| precedence declarations. It has already been checked that the |
@@ -263,66 +277,73 @@ resolve_sr_conflict (state *s, int ruleno, symbol **errors, int *nerrs)
reductions *reds = s->reductions;
/* Find the rule to reduce by to get precedence of reduction. */
rule *redrule = reds->rules[ruleno];
int redprec = redrule->prec->prec;
prec_node *redprecsym = redrule->prec->prec_node;
bitset lookahead_tokens = reds->lookahead_tokens[ruleno];
for (i = 0; i < ntokens; i++)
if (bitset_test (lookahead_tokens, i)
&& bitset_test (lookahead_set, i)
&& symbols[i]->content->prec)
&& bitset_test (lookahead_set, i))
{
/* Shift-reduce conflict occurs for token number i
and it has a precedence.
The precedence of shifting is that of token i. */
if (symbols[i]->content->prec < redprec)
if (redprecsym && symbols[i]->content->prec_node)
{
register_precedence (redrule->prec->number, i);
log_resolution (redrule, i, reduce_resolution);
flush_shift (s, i);
}
else if (symbols[i]->content->prec > redprec)
{
register_precedence (i, redrule->prec->number);
log_resolution (redrule, i, shift_resolution);
flush_reduce (lookahead_tokens, i);
/* Shift-reduce conflict occurs for token number i
and it has a precedence.
The precedence of shifting is that of token i. */
if (is_prec_superior (redprecsym, symbols[i]->content->prec_node))
{
register_precedence (redrule->prec->number, i);
log_resolution (redrule, i, reduce_resolution);
flush_shift (s, i);
}
else if (is_prec_superior (symbols[i]->content->prec_node,
redprecsym))
{
register_precedence (i, redrule->prec->number);
log_resolution (redrule, i, shift_resolution);
flush_reduce (lookahead_tokens, i);
}
else if (is_prec_equal (redprecsym, symbols[i]->content->prec_node))
/* Matching precedence levels.
For non-defined associativity, keep both: unexpected
associativity conflict.
For left associativity, keep only the reduction.
For right associativity, keep only the shift.
For nonassociativity, keep neither. */
switch (symbols[i]->content->prec_node->assoc)
{
case undef_assoc:
break;
case precedence_assoc:
break;
case right_assoc:
register_assoc (i, redrule->prec->number);
log_resolution (redrule, i, right_resolution);
flush_reduce (lookahead_tokens, i);
break;
case left_assoc:
register_assoc (i, redrule->prec->number);
log_resolution (redrule, i, left_resolution);
flush_shift (s, i);
break;
case non_assoc:
register_assoc (i, redrule->prec->number);
log_resolution (redrule, i, nonassoc_resolution);
flush_shift (s, i);
flush_reduce (lookahead_tokens, i);
/* Record an explicit error for this token. */
errors[(*nerrs)++] = symbols[i];
break;
}
else
log_resolution (redrule, i, uncomparable_resolution);
}
else
/* Matching precedence levels.
For non-defined associativity, keep both: unexpected
associativity conflict.
For left associativity, keep only the reduction.
For right associativity, keep only the shift.
For nonassociativity, keep neither. */
switch (symbols[i]->content->assoc)
{
case undef_assoc:
abort ();
case precedence_assoc:
break;
case right_assoc:
register_assoc (i, redrule->prec->number);
log_resolution (redrule, i, right_resolution);
flush_reduce (lookahead_tokens, i);
break;
case left_assoc:
register_assoc (i, redrule->prec->number);
log_resolution (redrule, i, left_resolution);
flush_shift (s, i);
break;
case non_assoc:
register_assoc (i, redrule->prec->number);
log_resolution (redrule, i, nonassoc_resolution);
flush_shift (s, i);
flush_reduce (lookahead_tokens, i);
/* Record an explicit error for this token. */
errors[(*nerrs)++] = symbols[i];
break;
}
log_resolution (redrule, i, uncomparable_resolution);
}
}
@@ -354,7 +375,7 @@ set_conflicts (state *s, symbol **errors)
check for shift-reduce conflict, and try to resolve using
precedence. */
for (i = 0; i < reds->num; ++i)
if (reds->rules[i]->prec && reds->rules[i]->prec->prec
if (reds->rules[i]->prec /* && reds->rules[i]->prec->prec */
&& !bitset_disjoint_p (reds->lookahead_tokens[i], lookahead_set))
resolve_sr_conflict (s, i, errors, &nerrs);
+4 -2
View File
@@ -44,6 +44,8 @@ int nvars = 0;
symbol_number *token_translations = NULL;
enum braces_state prec_braces = 0;
int max_user_token_number = 256;
bool
@@ -239,7 +241,7 @@ grammar_dump (FILE *out, const char *title)
for (i = ntokens; i < nsyms; i++)
fprintf (out, "%5d %5d %5d %s\n",
i,
symbols[i]->content->prec, symbols[i]->content->assoc,
symbols[i]->content->prec, symbols[i]->content->prec_node->assoc,
symbols[i]->tag);
fprintf (out, "\n\n");
}
@@ -262,7 +264,7 @@ grammar_dump (FILE *out, const char *title)
fprintf (out, "%3d (%2d, %2d, %2d, %2u-%2u) %2d ->",
i,
rule_i->prec ? rule_i->prec->prec : 0,
rule_i->prec ? rule_i->prec->assoc : 0,
rule_i->prec ? rule_i->prec->prec_node->assoc : 0,
rule_i->useful,
rhs_itemno,
rhs_itemno + rhs_count - 1,
+11
View File
@@ -117,6 +117,17 @@ typedef int item_number;
extern item_number *ritem;
extern unsigned int nritems;
enum braces_state
{
default_braces_state,
gprec_seen,
group_name_seen,
braces_seen
};
/* Marker for the lexer and parser, to correctly interpret braces. */
extern enum braces_state prec_braces;
/* There is weird relationship between OT1H item_number and OTOH
symbol_number and rule_number: we store the latter in
item_number. symbol_number values are stored as-is, while
+787 -652
View File
File diff suppressed because it is too large Load Diff
+64 -57
View File
@@ -52,7 +52,7 @@ extern int gram_debug;
#include "symlist.h"
#include "symtab.h"
#line 221 "src/parse-gram.y" /* yacc.c:1909 */
#line 233 "src/parse-gram.y" /* yacc.c:1909 */
typedef enum
{
@@ -61,7 +61,7 @@ extern int gram_debug;
param_parse = 1 << 1,
param_both = param_lex | param_parse
} param_type;
#line 645 "src/parse-gram.y" /* yacc.c:1909 */
#line 723 "src/parse-gram.y" /* yacc.c:1909 */
#include "muscle-tab.h"
#line 68 "src/parse-gram.h" /* yacc.c:1909 */
@@ -84,49 +84,54 @@ extern int gram_debug;
PERCENT_PRECEDENCE = 267,
PERCENT_PREC = 268,
PERCENT_DPREC = 269,
PERCENT_MERGE = 270,
PERCENT_CODE = 271,
PERCENT_DEFAULT_PREC = 272,
PERCENT_DEFINE = 273,
PERCENT_DEFINES = 274,
PERCENT_ERROR_VERBOSE = 275,
PERCENT_EXPECT = 276,
PERCENT_EXPECT_RR = 277,
PERCENT_FLAG = 278,
PERCENT_FILE_PREFIX = 279,
PERCENT_GLR_PARSER = 280,
PERCENT_INITIAL_ACTION = 281,
PERCENT_LANGUAGE = 282,
PERCENT_NAME_PREFIX = 283,
PERCENT_NO_DEFAULT_PREC = 284,
PERCENT_NO_LINES = 285,
PERCENT_NONDETERMINISTIC_PARSER = 286,
PERCENT_OUTPUT = 287,
PERCENT_REQUIRE = 288,
PERCENT_SKELETON = 289,
PERCENT_START = 290,
PERCENT_TOKEN_TABLE = 291,
PERCENT_VERBOSE = 292,
PERCENT_YACC = 293,
BRACED_CODE = 294,
BRACED_PREDICATE = 295,
BRACKETED_ID = 296,
CHAR = 297,
EPILOGUE = 298,
EQUAL = 299,
ID = 300,
ID_COLON = 301,
PERCENT_PERCENT = 302,
PIPE = 303,
PROLOGUE = 304,
SEMICOLON = 305,
TAG = 306,
TAG_ANY = 307,
TAG_NONE = 308,
INT = 309,
PERCENT_PARAM = 310,
PERCENT_UNION = 311,
PERCENT_EMPTY = 312
PERCENT_GPREC = 270,
PERCENT_PRECR = 271,
PERCENT_MERGE = 272,
PERCENT_CODE = 273,
PERCENT_DEFAULT_PREC = 274,
PERCENT_DEFINE = 275,
PERCENT_DEFINES = 276,
PERCENT_ERROR_VERBOSE = 277,
PERCENT_EXPECT = 278,
PERCENT_EXPECT_RR = 279,
PERCENT_FLAG = 280,
PERCENT_FILE_PREFIX = 281,
PERCENT_GLR_PARSER = 282,
PERCENT_INITIAL_ACTION = 283,
PERCENT_LANGUAGE = 284,
PERCENT_NAME_PREFIX = 285,
PERCENT_NO_DEFAULT_PREC = 286,
PERCENT_NO_LINES = 287,
PERCENT_NONDETERMINISTIC_PARSER = 288,
PERCENT_OUTPUT = 289,
PERCENT_REQUIRE = 290,
PERCENT_SKELETON = 291,
PERCENT_START = 292,
PERCENT_TOKEN_TABLE = 293,
PERCENT_VERBOSE = 294,
PERCENT_YACC = 295,
BRACED_CODE = 296,
BRACED_PREDICATE = 297,
BRACKETED_ID = 298,
CHAR = 299,
EPILOGUE = 300,
EQUAL = 301,
ID = 302,
ID_COLON = 303,
PERCENT_PERCENT = 304,
PIPE = 305,
PROLOGUE = 306,
SEMICOLON = 307,
GT = 308,
TAG = 309,
TAG_ANY = 310,
TAG_NONE = 311,
LBRACE = 312,
RBRACE = 313,
INT = 314,
PERCENT_PARAM = 315,
PERCENT_UNION = 316,
PERCENT_EMPTY = 317
};
#endif
@@ -135,27 +140,29 @@ extern int gram_debug;
typedef union GRAM_STYPE GRAM_STYPE;
union GRAM_STYPE
{
#line 182 "src/parse-gram.y" /* yacc.c:1909 */
#line 187 "src/parse-gram.y" /* yacc.c:1909 */
unsigned char character;
#line 186 "src/parse-gram.y" /* yacc.c:1909 */
char *code;
#line 191 "src/parse-gram.y" /* yacc.c:1909 */
char *code;
#line 196 "src/parse-gram.y" /* yacc.c:1909 */
uniqstr uniqstr;
#line 199 "src/parse-gram.y" /* yacc.c:1909 */
#line 204 "src/parse-gram.y" /* yacc.c:1909 */
int integer;
#line 203 "src/parse-gram.y" /* yacc.c:1909 */
symbol *symbol;
#line 208 "src/parse-gram.y" /* yacc.c:1909 */
symbol *symbol;
#line 213 "src/parse-gram.y" /* yacc.c:1909 */
assoc assoc;
#line 211 "src/parse-gram.y" /* yacc.c:1909 */
#line 216 "src/parse-gram.y" /* yacc.c:1909 */
symbol_list *list;
#line 214 "src/parse-gram.y" /* yacc.c:1909 */
#line 219 "src/parse-gram.y" /* yacc.c:1909 */
named_ref *named_ref;
#line 241 "src/parse-gram.y" /* yacc.c:1909 */
#line 224 "src/parse-gram.y" /* yacc.c:1909 */
prec_rel_comparator prec_rel_comparator;
#line 253 "src/parse-gram.y" /* yacc.c:1909 */
param_type param;
#line 409 "src/parse-gram.y" /* yacc.c:1909 */
#line 423 "src/parse-gram.y" /* yacc.c:1909 */
code_props_type code_type;
#line 647 "src/parse-gram.y" /* yacc.c:1909 */
#line 725 "src/parse-gram.y" /* yacc.c:1909 */
struct
{
@@ -163,7 +170,7 @@ code_props_type code_type;
muscle_kind kind;
} value;
#line 167 "src/parse-gram.h" /* yacc.c:1909 */
#line 174 "src/parse-gram.h" /* yacc.c:1909 */
};
# define GRAM_STYPE_IS_TRIVIAL 1
# define GRAM_STYPE_IS_DECLARED 1
+78
View File
@@ -130,6 +130,8 @@
%token PERCENT_PREC "%prec"
%token PERCENT_DPREC "%dprec"
%token PERCENT_GPREC "%gprec"
%token PERCENT_PRECR "%precr"
%token PERCENT_MERGE "%merge"
/*----------------------.
@@ -175,9 +177,12 @@
%token PIPE "|"
%token PROLOGUE "%{...%}"
%token SEMICOLON ";"
%token GT ">"
%token TAG "<tag>"
%token TAG_ANY "<*>"
%token TAG_NONE "<>"
%token LBRACE "{"
%token RBRACE "}"
%union {unsigned char character;}
%type <character> CHAR
@@ -214,6 +219,13 @@
%union {named_ref *named_ref;}
%type <named_ref> named_ref.opt
%type <uniqstr> prec_group_name.opt string_or_id
%union {prec_rel_comparator prec_rel_comparator;}
%type <prec_rel_comparator> prec_rel_comparator
%type <list> precedence_relation_symbols precedence_symbol
/*---------.
| %param. |
`---------*/
@@ -365,6 +377,8 @@ params:
grammar_declaration:
precedence_declaration
| precedence_group_declaration
| precedence_relation_declaration
| symbol_declaration
| "%start" symbol
{
@@ -457,6 +471,30 @@ symbol_declaration:
}
;
/* A group of symbols for precedence declaration */
precedence_group_declaration:
"%gprec" prec_group_name.opt
{
set_current_group ($2, &@2);
}
"{" precedence_declarations "}"
{
set_current_group (DEFAULT_GROUP_NAME, NULL);
}
;
/* Name for the precedence group. If none is present a new unique one is
generated. */
prec_group_name.opt:
%empty { $$ = new_anonymous_group_name (); }
| variable /* Just a string, maybe there's a better way? */
;
precedence_declarations:
precedence_declaration
| precedence_declarations precedence_declaration
;
precedence_declaration:
precedence_declarator tag.opt symbols.prec
{
@@ -484,6 +522,46 @@ tag.opt:
| TAG { current_type = $1; tag_seen = true; }
;
/* Declaration of a precedence relation between two (lists of) tokens */
precedence_relation_declaration:
"%precr" precedence_relation_symbols
{ prec_braces = default_braces_state; }
prec_rel_comparator
precedence_relation_symbols
{ declare_precedence_relation ($2, $5, $4, @4); }
;
precedence_relation_symbols:
precedence_symbol { $$ = $1; }
| precedence_relation_symbols precedence_symbol
{ $$ = symbol_list_append ($1, $2); }
;
precedence_symbol:
string_or_id
{
if (is_prec_group ($1))
$$ = expand_symbol_group (symgroup_from_uniqstr($1, &@1), @1);
else
$$ = symbol_list_sym_new (symbol_from_uniqstr ($1, @1), @1);
}
| CHAR
{
$$ = symbol_list_sym_new (symbol_from_uniqstr (uniqstr_new (char_name ($1)), @1), @1);
}
;
string_or_id:
STRING { $$ = uniqstr_new (quotearg_style (c_quoting_style, $1)); }
| ID { $$ = $1; }
;
prec_rel_comparator:
">" { $$ = prec_superior; }
| "=" { $$ = prec_equal; }
| ">" ">" { $$ = prec_superior_strict; }
;
/* Just like symbols.1 but accept INT for the sake of POSIX. */
symbols.prec:
symbol.prec
+2 -1
View File
@@ -392,7 +392,8 @@ print_grammar (FILE *out, int level)
{
char const *tag = symbols[token_translations[i]]->tag;
int precedence = symbols[token_translations[i]]->content->prec;
assoc associativity = symbols[token_translations[i]]->content->assoc;
assoc associativity = symbols[token_translations[i]]->content->prec_node
->assoc;
xml_indent (out, level + 2);
fprintf (out,
"<terminal symbol-number=\"%d\" token-number=\"%d\""
+17
View File
@@ -223,6 +223,10 @@ eqopt ([[:space:]]*=)?
"%fixed-output-files" return PERCENT_YACC;
"%initial-action" return PERCENT_INITIAL_ACTION;
"%glr-parser" return PERCENT_GLR_PARSER;
"%gprec" {
prec_braces = gprec_seen;
return PERCENT_GPREC;
}
"%language" return PERCENT_LANGUAGE;
"%left" return PERCENT_LEFT;
"%lex-param" RETURN_PERCENT_PARAM(lex);
@@ -239,6 +243,7 @@ eqopt ([[:space:]]*=)?
"%parse-param" RETURN_PERCENT_PARAM(parse);
"%prec" return PERCENT_PREC;
"%precedence" return PERCENT_PRECEDENCE;
"%precr" return PERCENT_PRECR;
"%printer" return PERCENT_PRINTER;
"%pure-parser" RETURN_PERCENT_FLAG("api.pure");
"%require" return PERCENT_REQUIRE;
@@ -273,10 +278,17 @@ eqopt ([[:space:]]*=)?
"=" return EQUAL;
"|" return PIPE;
";" return SEMICOLON;
"}" return RBRACE;
">" return GT;
{id} {
val->uniqstr = uniqstr_new (yytext);
id_loc = *loc;
if (prec_braces == gprec_seen)
{
prec_braces = group_name_seen;
return ID;
}
bracketed_id_str = NULL;
BEGIN SC_AFTER_IDENTIFIER;
}
@@ -307,6 +319,11 @@ eqopt ([[:space:]]*=)?
/* Code in between braces. */
"{" {
if (prec_braces == gprec_seen || prec_braces == group_name_seen)
{
prec_braces = braces_seen;
return LBRACE;
}
STRING_GROW;
nesting = 0;
code_start = loc->start;
+483 -12
View File
@@ -26,6 +26,7 @@
#include "complain.h"
#include "gram.h"
#include "symtab.h"
#include "symlist.h"
/*-------------------------------------------------------------------.
| Symbols sorted by tag. Allocated by the first invocation of |
@@ -58,6 +59,305 @@ static symgraph **prec_nodes;
bool *used_assoc = NULL;
/*-------------------------------------------------------------.
| The current precedence group of symbols. Used by the parser. |
`-------------------------------------------------------------*/
static symgroup *current_group = NULL;
/*-------------------------------------------------------.
| The list of symbols declared in the current statement. |
`-------------------------------------------------------*/
static symbol_list *current_prec_declaration = NULL;
/*-------------------------------------------------.
| A counter to distinguish precedence declarations |
`-------------------------------------------------*/
static int current_prec_level = 0;
/*-----------------------------.
| Constructor for a prec_link. |
`-----------------------------*/
static prec_link *
prec_link_new (prec_node *to, bool transitive)
{
prec_link *res = malloc (sizeof *res);
res->target = to;
res->transitive = transitive;
res->next = NULL;
return res;
}
/*-------------------------------------.
| Destructor for a simple symbol list. |
`-------------------------------------*/
static void
symbol_list_prec_free (symbol_list *l)
{
if (l)
{
symbol_list_prec_free (l->next);
free (l);
}
}
/*------------------------------------------------.
| Check if PARENT has a higher priority than SON. |
`------------------------------------------------*/
bool
is_prec_superior (prec_node *parent, prec_node *son)
{
prec_link *l;
for (l = parent->sons; l; l = l->next)
if (l->target == son)
return true;
return false;
}
/*-----------------------------------------.
| Check if S1 has the same priority as S2. |
`-----------------------------------------*/
bool
is_prec_equal (prec_node *s1, prec_node *s2)
{
prec_link *l;
if (s1 == s2)
return true;
for (l = s1->equals; l; l = l->next)
if (l->target == s2)
return true;
return false;
}
static inline void
complain_contradicting_prec (location *loc, uniqstr s1, uniqstr s2, char c1,
char c2)
{
complain (loc, Wprecedence, _("contradicting declaration: %s %c %s is in "
"conflict with the previous declaration: %s %c %s"), s1, c1, s2,
s1, c2, s2);
}
/*-----------------------------------------------------------------------.
| Compare LINK with TARGET, and return whether they are equal. |
| In case of equality, complain of the duplicate precedence declaration. |
`-----------------------------------------------------------------------*/
static inline bool
is_prec_target (prec_node *l, prec_node *target, uniqstr from, char c,
location loc)
{
if (l == target)
{
complain (&loc, Wprecedence, _("duplicate declaration of the precedence "
"relationship %s %c %s"), from, c,
target->symbol->tag);
return true;
}
return false;
}
/*-----------------------------------------.
| Add a precedence relationship FROM > TO. |
`-----------------------------------------*/
static void
add_prec_link (prec_node *from, prec_node *to, bool transitive, location loc)
{
if (is_prec_superior (to, from))
complain_contradicting_prec(&loc, from->symbol->tag, to->symbol->tag,
'>', '<');
else if (is_prec_equal (from, to))
complain_contradicting_prec(&loc, from->symbol->tag, to->symbol->tag,
'>', '=');
else
{
if (from->sons)
{
if (is_prec_target (from->sons->target, to, from->symbol->tag, '>',
loc))
return;
prec_link *son = from->sons;
for (; son->next; son = son->next)
if (is_prec_target (son->next->target, to, from->symbol->tag, '>',
loc))
return;
son->next = prec_link_new (to, transitive);
}
else
from->sons = prec_link_new (to, transitive);
}
}
/*-------------------------------------------------.
| Add a precedence relationship S1 == S2, one way. |
`-------------------------------------------------*/
static void
create_prec_equal_link (prec_node *s1, prec_node *s2, bool transitive,
location loc)
{
if (s1->equals)
{
if (is_prec_target (s1->equals->target, s2, s1->symbol->tag, '=',
loc))
return;
prec_link *eq = s1->equals;
for (; eq->next; eq = eq->next)
if (is_prec_target (eq->next->target, s2, s1->symbol->tag, '=',
loc))
return;
eq->next = prec_link_new (s2, transitive);
}
else
s1->equals = prec_link_new (s2, transitive);
}
/*---------------------------------------------------.
| Add a precedence relationship S1 == S2, both ways. |
`---------------------------------------------------*/
static void
add_prec_equal_link (prec_node *s1, prec_node *s2, bool transitive,
location loc)
{
if (is_prec_superior (s2, s1))
complain_contradicting_prec(&loc, s1->symbol->tag, s2->symbol->tag,
'=', '>');
else if (is_prec_superior (s1, s2))
complain_contradicting_prec(&loc, s1->symbol->tag, s2->symbol->tag,
'=', '<');
create_prec_equal_link (s1, s2, transitive, loc);
create_prec_equal_link (s2, s1, transitive, loc);
}
/* The function to use to register \a c type relations. */
typedef void (*add_link_t) (prec_node *, prec_node *, bool, location);
static add_link_t
add_link_function (prec_rel_comparator c)
{
switch (c)
{
case prec_superior_strict:
case prec_superior:
return &add_prec_link;
case prec_equal:
return &add_prec_equal_link;
}
abort ();
}
/*---------------------------------------------------------------------.
| Handle the precedence declaration between the elements of S1 and S2. |
`---------------------------------------------------------------------*/
void
declare_precedence_relation (symbol_list *s1, symbol_list *s2,
prec_rel_comparator c, location loc)
{
void (*functionPtr) (prec_node *, prec_node *, bool, location)
= add_link_function (c);
bool transitive = c != prec_superior_strict;
for (symbol_list *l1 = s1; l1; l1 = l1->next)
for (symbol_list *l2 = s2; l2; l2 = l2->next)
(*functionPtr)(l1->content.sym->content->prec_node,
l2->content.sym->content->prec_node, transitive, loc);
symbol_list_free (s1);
symbol_list_free (s2);
}
/*----------------------------------------------.
| Get the list of symbols contained in a group. |
`----------------------------------------------*/
symbol_list *
expand_symbol_group (symgroup *group, location loc)
{
symbol_list *l = NULL;
for (sym_content *s = group->symbol_list; s; s = s->group_next)
l = symbol_list_append (l, symbol_list_sym_new (s->symbol, loc));
return l;
}
/*------------------------------------------------------------------------.
| Add a symbol to the current declaration group, and declare the implicit |
| precedence links. SAME_LINE is true if the symbol was declared in the |
| same statement as the previous one (same precedence level). |
`------------------------------------------------------------------------*/
void
add_to_current_group (sym_content *s, bool same_line)
{
if (!same_line)
for (symbol_list *l = current_prec_declaration; l; l = l->next)
{
sym_content *symb = l->content.sym->content;
if (!current_group->symbol_list)
current_group->symbol_list = symb;
else
{
sym_content *sym = current_group->symbol_list;
while (sym->group_next)
sym = sym->group_next;
sym->group_next = symb;
}
}
if (current_group->symbol_list)
for (sym_content *sym = current_group->symbol_list; sym;
sym = sym->group_next)
add_prec_link (s->prec_node, sym->prec_node, true,
s->prec_node->prec_location);
if (!same_line)
{
symbol_list_prec_free (current_prec_declaration);
current_prec_declaration = malloc (sizeof *current_prec_declaration);
current_prec_declaration->content.sym = s->symbol;
current_prec_declaration->next = NULL;
}
else
{
symbol_list *l = current_prec_declaration;
for (; true; l = l->next)
{
add_prec_equal_link (s->prec_node, l->content.sym->content->prec_node,
true, s->prec_node->prec_location);
if (!l->next)
break;
}
l->next = malloc (sizeof *l->next);
l->next->content.sym = s->symbol;
l->next->next = NULL;
}
}
/*-----------------------------------------.
| Create a new prec_node for the symbol s. |
`-----------------------------------------*/
static prec_node *
prec_node_new (symbol * s)
{
prec_node * res = malloc (sizeof *res);
res->symbol = s;
res->assoc = undef_assoc;
res->sons = NULL;
res-> equals = NULL;
return res;
}
/*--------------------------.
| Create a new sym_content. |
`--------------------------*/
@@ -78,12 +378,14 @@ sym_content_new (symbol *s)
res->number = NUMBER_UNDEFINED;
res->prec = 0;
res->assoc = undef_assoc;
res->user_token_number = USER_NUMBER_UNDEFINED;
res->class = unknown_sym;
res->status = undeclared;
res->group_next = NULL;
res->prec_node = prec_node_new (s);
return res;
}
@@ -105,6 +407,7 @@ symbol_new (uniqstr tag, location loc)
res->tag = tag;
res->location = loc;
res->alias = NULL;
res->content = sym_content_new (res);
res->is_alias = false;
@@ -116,6 +419,28 @@ symbol_new (uniqstr tag, location loc)
return res;
}
void
prec_link_free (prec_link * l)
{
if (l)
{
prec_link_free (l->next);
free (l);
}
}
/*--------------------.
| Free one prec_node. |
`--------------------*/
static void
prec_node_free (prec_node * n)
{
prec_link_free (n->sons);
prec_link_free (n->equals);
free (n);
}
/*--------------------.
| Free a sym_content. |
`--------------------*/
@@ -123,6 +448,7 @@ symbol_new (uniqstr tag, location loc)
static void
sym_content_free (sym_content *sym)
{
prec_node_free (sym->prec_node);
free (sym);
}
@@ -367,14 +693,16 @@ symbol_precedence_set (symbol *sym, int prec, assoc a, location loc)
sym_content *s = sym->content;
if (a != undef_assoc)
{
if (s->prec)
if (s->prec_node->assoc != undef_assoc)
symbol_redeclaration (sym, assoc_to_string (a),
s->prec_location, loc);
s->prec_node->prec_location, loc);
else
{
s->prec = prec;
s->assoc = a;
s->prec_location = loc;
s->prec_node->assoc = a;
s->prec_node->prec_location = loc;
add_to_current_group (s, prec == current_prec_level);
current_prec_level = prec;
}
}
@@ -684,6 +1012,38 @@ hash_semantic_type_hasher (void const *m, size_t tablesize)
return hash_semantic_type (m, tablesize);
}
/*-------------------------------------.
| Symbol precedence group hash table. |
`-------------------------------------*/
static struct hash_table *group_table = NULL;
static inline bool
hash_compare_group (const symgroup *m1, const symgroup *m2)
{
/* Since tags are unique, we can compare the pointers themselves. */
return UNIQSTR_EQ (m1->tag, m2->tag);
}
static bool
hash_group_comparator (void const *m1, void const *m2)
{
return hash_compare_group (m1, m2);
}
static inline size_t
hash_group (const symgroup *m, size_t tablesize)
{
/* Since tags are unique, we can hash the pointer itself. */
return ((uintptr_t) m->tag) % tablesize;
}
static size_t
hash_group_hasher (void const *m, size_t tablesize)
{
return hash_group (m, tablesize);
}
/*-------------------------------.
| Create the symbol hash table. |
`-------------------------------*/
@@ -701,6 +1061,12 @@ symbols_new (void)
hash_semantic_type_hasher,
hash_semantic_type_comparator,
free);
group_table = hash_initialize (HT_INITIAL_CAPACITY,
NULL,
hash_group_hasher,
hash_group_comparator,
free);
set_current_group (DEFAULT_GROUP_NAME, NULL);
}
@@ -815,9 +1181,11 @@ symbols_free (void)
{
hash_free (symbol_table);
hash_free (semantic_type_table);
hash_free (group_table);
free (symbols);
free (symbols_sorted);
free (semantic_types_sorted);
symbol_list_prec_free (current_prec_declaration);
}
@@ -1102,8 +1470,8 @@ static inline bool
is_assoc_useless (symbol *s)
{
return s
&& s->content->assoc != undef_assoc
&& s->content->assoc != precedence_assoc
&& s->content->prec_node->assoc != undef_assoc
&& s->content->prec_node->assoc != precedence_assoc
&& !used_assoc[s->content->number];
}
@@ -1136,21 +1504,124 @@ print_precedence_warnings (void)
{
symbol *s = symbols[i];
if (s
&& s->content->prec != 0
&& !prec_nodes[i]->pred
&& !prec_nodes[i]->succ)
{
if (is_assoc_useless (s))
complain (&s->content->prec_location, Wprecedence,
complain (&s->content->prec_node->prec_location, Wprecedence,
_("useless precedence and associativity for %s"), s->tag);
else if (s->content->assoc == precedence_assoc)
complain (&s->content->prec_location, Wprecedence,
else if (s->content->prec_node->assoc == precedence_assoc)
complain (&s->content->prec_node->prec_location, Wprecedence,
_("useless precedence for %s"), s->tag);
}
else if (is_assoc_useless (s))
complain (&s->content->prec_location, Wprecedence,
complain (&s->content->prec_node->prec_location, Wprecedence,
_("useless associativity for %s, use %%precedence"), s->tag);
}
free (used_assoc);
assoc_free ();
}
/*------------------------------------------------.
| Counter to create unique anonymous group names. |
`------------------------------------------------*/
static unsigned int anon_group_counter = 0;
/*-------------------------------------------------.
| Return a new unique name for an anonymous group. |
`-------------------------------------------------*/
uniqstr new_anonymous_group_name (void)
{
char buff[20];
snprintf (buff, 20, "__anon%u__", anon_group_counter++);
return uniqstr_new (buff);
}
/*-------------------------------------------.
| Constructor for a symbol precedence group. |
`-------------------------------------------*/
symgroup *
symgroup_new (const uniqstr tag, location loc)
{
symgroup *group = xmalloc (sizeof (*group));
group->tag = tag;
group->symbol_list = NULL;
group->location = loc;
return group;
}
/*----------------------------------------.
| Check if there is a group by that name. |
`----------------------------------------*/
bool
is_prec_group (const uniqstr key)
{
symgroup probe;
symgroup *entry;
probe.tag = key;
entry = hash_lookup (group_table, &probe);
return entry != NULL;
}
/*--------------------------------------------------------------------------.
| Get the symbol precedence group by that name. If not present, a new group |
| is created and inserted in the table, with the location information |
| provided, if any. |
`--------------------------------------------------------------------------*/
symgroup *
symgroup_from_uniqstr (const uniqstr key, location *loc)
{
bool null_loc = loc == NULL;
if (null_loc)
{
loc = malloc (sizeof *loc);
boundary_set (&loc->start, uniqstr_new (""), 1, 1);
boundary_set (&loc->end, uniqstr_new (""), 1, 1);
}
symgroup probe;
symgroup *entry;
probe.tag = key;
entry = hash_lookup (group_table, &probe);
if (!entry)
{
/* First insertion in the hash. */
entry = symgroup_new (key, *loc);
if (!hash_insert (group_table, entry))
xalloc_die ();
}
if (null_loc)
free (loc);
return entry;
}
/*--------------------------------------------------------------------------.
| Change the current group to the one designated by the name, and create it |
| if necessary. The location information is used for creation if available. |
`--------------------------------------------------------------------------*/
void set_current_group (const uniqstr tag, location *loc)
{
for (symbol_list *l = current_prec_declaration; l; l = l->next)
{
sym_content *symb = l->content.sym->content;
if (!current_group->symbol_list)
current_group->symbol_list = symb;
else
{
sym_content *sym = current_group->symbol_list;
for (; sym->group_next; sym = sym->group_next)
{}
sym->group_next = symb;
}
}
symbol_list_prec_free (current_prec_declaration);
current_prec_declaration = NULL;
current_group = symgroup_from_uniqstr (tag, loc);
}
+99 -2
View File
@@ -31,6 +31,8 @@
# include "scan-code.h"
# include "uniqstr.h"
typedef struct symbol_list symbol_list;
/*----------.
| Symbols. |
`----------*/
@@ -62,6 +64,8 @@ typedef struct sym_content sym_content;
When status are checked at the end, "declared" symbols are fine,
"used" symbols trigger warnings, otherwise it's an error. */
typedef struct prec_node prec_node;
typedef enum
{
/** Used in the input file for an unknown reason (error). */
@@ -125,13 +129,21 @@ struct sym_content
code_props props[CODE_PROPS_SIZE];
symbol_number number;
location prec_location;
/* Not used anymore, to remove. */
int prec;
assoc assoc;
int user_token_number;
symbol_class class;
status status;
/* The next element in the symbol precedence group. */
sym_content *group_next;
/* The graph node containing all the precedence information for this
symbol. */
prec_node *prec_node;
};
/** Undefined user number. */
@@ -286,6 +298,91 @@ void print_precedence_warnings (void);
void register_assoc (graphid i, graphid j);
/*------------------.
| Groups of symbols |
`------------------*/
#define DEFAULT_GROUP_NAME uniqstr_new ("__default__")
typedef struct symgroup symgroup;
struct symgroup
{
/** The name of the group. */
uniqstr tag;
/** The list of symbols in the group. */
sym_content * symbol_list;
location location;
} ;
/** Get a dummy name for an anonymous group. */
uniqstr new_anonymous_group_name (void);
/** Set the current group in the token precedence declaration to a new group
* with this name */
void set_current_group (const uniqstr name, location *loc);
/** Get or create the group by that name. The location information is used for
* creation when available. */
symgroup *
symgroup_from_uniqstr (const uniqstr key, location *loc);
/** Check if there is a symbol precedence group by that name. */
bool
is_prec_group (const uniqstr key);
/*----------------------------------.
| Graph of precedence relationships |
`----------------------------------*/
typedef struct prec_link prec_link;
struct prec_link
{
prec_node *target;
bool transitive;
prec_link *next;
};
struct prec_node
{
symbol *symbol;
/** Associativity for the symbol. */
assoc assoc;
location prec_location;
prec_link *sons;
prec_link *equals;
};
typedef enum prec_rel_comparator prec_rel_comparator;
enum prec_rel_comparator
{
prec_equal,
prec_superior,
prec_superior_strict,
};
/** Declare a precedence relationship between the symbols of the two lists,
* as defined by the operator. */
void
declare_precedence_relation (symbol_list *l1, symbol_list *l2,
prec_rel_comparator c, location loc);
/** Return the list of symbols contained in the group. */
symbol_list *
expand_symbol_group (symgroup * group, location loc);
/** Check if s1 and s2 have the same precedence level. */
bool is_prec_equal (prec_node * s1, prec_node * s2);
/** Check if from > to . */
bool is_prec_superior (prec_node * from, prec_node * to);
/*-----------------.
| Semantic types. |
`-----------------*/
+146
View File
@@ -17,6 +17,152 @@
AT_BANNER([[Conflicts.]])
## ----------------- ##
## Precedence groups ##
## ----------------- ##
# Sample use case of precedence groups and relations, working.
AT_SETUP([Precedence groups])
AT_DATA_GRAMMAR([[input.y]],
[[%token CARET "^"
%token NUM BOOL '^' OR AND
%left '+' '-'
%gprec {
%right CARET
}
%gprec boolean {
%left OR
%left AND
}
%left '*' '/'
%precr boolean >> "^"
%precr CARET > '*' '/' '-' '+'
%%
stmt:
exp
| bool_exp
exp:
NUM
| exp '+' exp
| exp '-' exp
| exp '*' exp
| exp '/' exp
| exp "^" exp
bool_exp:
BOOL
| bool_exp AND bool_exp
| bool_exp OR bool_exp
| bool_exp CARET bool_exp
]])
AT_BISON_CHECK([[--report=all -o input.c input.y]], 0, [])
AT_CLEANUP
## -------------------------------- ##
## Conflicting precedence relations ##
## -------------------------------- ##
AT_SETUP([Conflicting precedence relations])
AT_DATA_GRAMMAR([[input.y]],
[[%token TOKEN
%precedence A
%precedence B
%precedence C
%precedence D E
%gprec group {
%precedence F
%precedence G
}
%precr B = C
%precr A > B
%precr C > B
%precr F > G
%precr F > A
%%
exp:
TOKEN
| exp A exp
| exp B exp
| exp C exp
| exp D exp
| exp E exp
| exp F exp
| exp G exp
]])
AT_BISON_CHECK([[-Wall -o input.c input.y]], 0, [],
[[input.y:20.10: warning: contradicting declaration: B = C is in conflict with the previous declaration: B > C [-Wprecedence]
input.y:21.10: warning: contradicting declaration: A > B is in conflict with the previous declaration: A < B [-Wprecedence]
input.y:22.10: warning: contradicting declaration: C > B is in conflict with the previous declaration: C = B [-Wprecedence]
input.y:23.10: warning: contradicting declaration: F > G is in conflict with the previous declaration: F < G [-Wprecedence]
input.y: warning: 27 shift/reduce conflicts [-Wconflicts-sr]
]])
AT_CLEANUP
## ------------------------------ ##
## Duplicate precedence relations ##
## ------------------------------ ##
AT_SETUP([Duplicate precedence relations])
AT_DATA_GRAMMAR([[input.y]],
[[%token TOKEN
%precedence A
%precedence B
%precedence C
%precedence D E
%gprec group {
%precedence F
%precedence G
}
%precr D = E
%precr B > A
%precr C > B
%precr G > F
%precr F > A
%precr C > group
%precr C > F
%%
exp:
TOKEN
| exp A exp
| exp B exp
| exp C exp
| exp D exp
| exp E exp
| exp F exp
| exp G exp
]])
AT_BISON_CHECK([[-Wall -o input.c input.y]], 0, [],
[[input.y:20.10: warning: duplicate declaration of the precedence relationship D = E [-Wprecedence]
input.y:20.10: warning: duplicate declaration of the precedence relationship E = D [-Wprecedence]
input.y:21.10: warning: duplicate declaration of the precedence relationship B > A [-Wprecedence]
input.y:22.10: warning: duplicate declaration of the precedence relationship C > B [-Wprecedence]
input.y:23.10: warning: duplicate declaration of the precedence relationship G > F [-Wprecedence]
input.y:26.10: warning: duplicate declaration of the precedence relationship C > F [-Wprecedence]
input.y: warning: 23 shift/reduce conflicts [-Wconflicts-sr]
]])
AT_CLEANUP
## ------------------------- ##
## Token declaration order. ##
## ------------------------- ##
+4 -4
View File
@@ -484,7 +484,7 @@ dnl - 61 -> 328: reduce -> shift on '*', '/', and '%'
NAME [reduce using rule 152 (opt_variable)]
'$' [reduce using rule 152 (opt_variable)]
@@ -5379,7 +5379,7 @@
@@ -5385,7 +5385,7 @@
156 | . '$' non_post_simp_exp
NAME shift, and go to state 9
@@ -493,7 +493,7 @@ dnl - 61 -> 328: reduce -> shift on '*', '/', and '%'
NAME [reduce using rule 152 (opt_variable)]
'$' [reduce using rule 152 (opt_variable)]
@@ -5399,7 +5399,7 @@
@@ -5405,7 +5405,7 @@
156 | . '$' non_post_simp_exp
NAME shift, and go to state 9
@@ -502,7 +502,7 @@ dnl - 61 -> 328: reduce -> shift on '*', '/', and '%'
NAME [reduce using rule 152 (opt_variable)]
'$' [reduce using rule 152 (opt_variable)]
@@ -6214,7 +6214,7 @@
@@ -6220,7 +6220,7 @@
156 | . '$' non_post_simp_exp
NAME shift, and go to state 9
@@ -511,7 +511,7 @@ dnl - 61 -> 328: reduce -> shift on '*', '/', and '%'
NAME [reduce using rule 152 (opt_variable)]
'$' [reduce using rule 152 (opt_variable)]
@@ -11099,3 +11099,274 @@
@@ -11117,3 +11117,274 @@
45 statement: LEX_FOR '(' opt_exp semi opt_nls exp semi opt_nls opt_exp r_paren opt_nls statement .
$default reduce using rule 45 (statement)
+1 -2
View File
@@ -64,14 +64,13 @@ AT_CHECK([[$PERL -pi -e 's/\\(\d{3})/chr(oct($1))/ge' input.y || exit 77]])
AT_BISON_CHECK([input.y], [1], [],
[[input.y:1.1-2: error: invalid characters: '\0\001\002\377?'
input.y:3.1: error: invalid character: '?'
input.y:4.14: error: invalid character: '}'
input.y:4.14: error: syntax error, unexpected }
input.y:5.1: error: invalid character: '%'
input.y:5.2: error: invalid character: '&'
input.y:6.1-17: error: invalid directive: '%a-does-not-exist'
input.y:7.1: error: invalid character: '%'
input.y:7.2: error: invalid character: '-'
input.y:8.1-9.0: error: missing '%}' at end of file
input.y:8.1-9.0: error: syntax error, unexpected %{...%}
]])
AT_CLEANUP
+1 -2
View File
@@ -405,14 +405,13 @@ default: 'a' }
AT_BISON_CHECK([input.y], [1], [],
[[input.y:2.1: error: invalid character: '?'
input.y:3.14: error: invalid character: '}'
input.y:3.14: error: syntax error, unexpected }
input.y:4.1: error: invalid character: '%'
input.y:4.2: error: invalid character: '&'
input.y:5.1-17: error: invalid directive: '%a-does-not-exist'
input.y:6.1: error: invalid character: '%'
input.y:6.2: error: invalid character: '-'
input.y:7.1-8.0: error: missing '%}' at end of file
input.y:7.1-8.0: error: syntax error, unexpected %{...%}
]])
AT_CLEANUP