%code top {
/*
* Parser for C declarations.
- * Copyright © 2011 Nick Bowler
+ * Copyright © 2011-2012, 2021, 2023 Nick Bowler
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*/
}
-%parse-param {yyscan_t scanner}
+%name-prefix "cdecl__yy"
+%parse-param {void *scanner}
%parse-param {struct cdecl **out}
%lex-param {yyscan_t scanner}
%define api.pure
%locations
%{
+#include <config.h>
#include <assert.h>
#include <stdbool.h>
#include "scan.h"
#include "cdecl.h"
+#include "cdecl-internal.h"
+#include "errmsg.h"
#define FAIL(msg) do { \
yyerror(&yylloc, NULL, NULL, msg); \
#define ALLOC(ptr, size) do { \
(ptr) = malloc(size); \
- if (!(ptr)) \
- FAIL("failed to allocate memory"); \
+ if (!(ptr)) { \
+ cdecl__errmsg(CDECL__ENOMEM); \
+ YYERROR; \
+ } \
} while (0)
#define ALLOC_STRUCT(ptr, type, ...) do { \
}
%code provides {
-void yyerror(YYLTYPE *, void *, struct cdecl **, const char *);
-int yyparse(void *scanner, struct cdecl **out);
+void cdecl__free(struct cdecl *);
+int cdecl__yyparse(void *scanner, struct cdecl **out);
+const char *cdecl__token_name(unsigned token);
}
%union {
uintmax_t uintval;
+ unsigned spectype;
_Bool boolval;
char *strval;
struct cdecl_declspec *declspec;
}
}
-void cdecl_free(struct cdecl *decl)
+void cdecl__free(struct cdecl *decl)
{
free_decl(decl);
}
+
+static void
+yyerror(YYLTYPE *loc, yyscan_t scanner, struct cdecl **out, const char *err)
+{
+ if (strstr(err, "T_LEX_ERROR"))
+ return;
+
+ cdecl__err(CDECL_ENOPARSE, "%s", err);
+}
%}
%destructor { free($$); } <strval>
%token T_COMMA ","
%token T_ELLIPSIS "..."
-%token T_TYPEDEF "typedef"
-%token T_EXTERN "extern"
-%token T_STATIC "static"
-%token T_AUTO "auto"
-%token T_REGISTER "register"
-
-%token T_INLINE "inline"
-
-%token T_RESTRICT "restrict"
-%token T_VOLATILE "volatile"
-%token T_CONST "const"
-
-%token T_VOID "void"
-%token T_CHAR "char"
-%token T_SHORT "short"
-%token T_INT "int"
-%token T_LONG "long"
-%token T_FLOAT "float"
-%token T_DOUBLE "double"
-%token T_SIGNED "signed"
-%token T_UNSIGNED "unsigned"
-%token T_BOOL "_Bool"
-%token T_COMPLEX "_Complex"
-%token T_IMAGINARY "_Imaginary"
-
-%token T_STRUCT "struct"
-%token T_UNION "union"
-%token T_ENUM "enum"
+%token <spectype> T_TYPEDEF "typedef"
+%token <spectype> T_EXTERN "extern"
+%token <spectype> T_STATIC "static"
+%token <spectype> T_AUTO "auto"
+%token <spectype> T_REGISTER "register"
+
+%token <spectype> T_INLINE "inline"
+
+%token <spectype> T_RESTRICT "restrict"
+%token <spectype> T_VOLATILE "volatile"
+%token <spectype> T_CONST "const"
+
+%token <spectype> T_VOID "void"
+%token <spectype> T_CHAR "char"
+%token <spectype> T_SHORT "short"
+%token <spectype> T_INT "int"
+%token <spectype> T_LONG "long"
+%token <spectype> T_FLOAT "float"
+%token <spectype> T_DOUBLE "double"
+%token <spectype> T_SIGNED "signed"
+%token <spectype> T_UNSIGNED "unsigned"
+%token <spectype> T_BOOL "_Bool"
+%token <spectype> T_COMPLEX "_Complex"
+%token <spectype> T_IMAGINARY "_Imaginary"
+
+%token <spectype> T_STRUCT "struct"
+%token <spectype> T_UNION "union"
+%token <spectype> T_ENUM "enum"
/*
* English keywords.
%type <strval> vla_ident
%type <boolval> varargs
-%type <uintval> declspec_simple typespec_simple qualifier_simple
+%type <spectype> declspec_simple qualifier_simple
+%type <spectype> typespec_simple typespec_tagged
%type <declspec> declspec_notype declspec_noid typespec_noid typespec
%type <declspec> qualifier qualifiers
%type <declspec> declspecs declspecs_noid
*out = $1;
};
-declaration: declspecs declarators T_SEMICOLON {
+semi: | T_SEMICOLON
+
+declaration: declspecs declarators semi {
$$ = $2;
for (struct cdecl *i = $$; i; i = i->next)
i->specifiers = $1;
};
+/*
+ * We support parsing declarations using arbitrary identifiers as type
+ * specifiers (a la C typedef). To avoid confusion with identifiers that
+ * may also be used as declarators, note the following:
+ *
+ * (a) Every valid C declaration must have at least one type specifier, and
+ * (b) Valid declarations with typedef names have exactly one type specifier.
+ *
+ * So the rule applied when parsing specifiers is: an identifier is a type
+ * specifier only if we have not yet seen any type specifiers whatsoever
+ * (within one declaration specifier list).
+ *
+ * Treating identifiers as type specifiers by default can lead to strange and
+ * unexpected parses; libcdecl applies a simplification step to the resulting
+ * parse tree afterwards.
+ */
declspecs: declspec_notype declspecs {
$$ = $1;
$$->next = $2;
ALLOC_STRUCT($$, struct cdecl, .declarators = $1);
}
-declspec_simple: T_AUTO { $$ = CDECL_STOR_AUTO; }
- | T_TYPEDEF { $$ = CDECL_STOR_TYPEDEF; }
- | T_EXTERN { $$ = CDECL_STOR_EXTERN; }
- | T_STATIC { $$ = CDECL_STOR_STATIC; }
- | T_REGISTER { $$ = CDECL_STOR_REGISTER; }
- | T_INLINE { $$ = CDECL_FUNC_INLINE; }
-
-typespec_simple: T_VOID { $$ = CDECL_TYPE_VOID; }
- | T_CHAR { $$ = CDECL_TYPE_CHAR; }
- | T_SHORT { $$ = CDECL_TYPE_SHORT; }
- | T_INT { $$ = CDECL_TYPE_INT; }
- | T_LONG { $$ = CDECL_TYPE_LONG; }
- | T_FLOAT { $$ = CDECL_TYPE_FLOAT; }
- | T_DOUBLE { $$ = CDECL_TYPE_DOUBLE; }
- | T_SIGNED { $$ = CDECL_TYPE_SIGNED; }
- | T_UNSIGNED { $$ = CDECL_TYPE_UNSIGNED; }
- | T_BOOL { $$ = CDECL_TYPE_BOOL; }
- | T_COMPLEX { $$ = CDECL_TYPE_COMPLEX; }
- | T_IMAGINARY { $$ = CDECL_TYPE_IMAGINARY; }
-
-qualifier_simple: T_CONST { $$ = CDECL_QUAL_CONST; }
- | T_RESTRICT { $$ = CDECL_QUAL_RESTRICT; }
- | T_VOLATILE { $$ = CDECL_QUAL_VOLATILE; }
+declspec_simple: T_AUTO
+ | T_TYPEDEF
+ | T_EXTERN
+ | T_STATIC
+ | T_REGISTER
+ | T_INLINE
+
+typespec_simple: T_VOID
+ | T_CHAR
+ | T_SHORT
+ | T_INT
+ | T_LONG
+ | T_FLOAT
+ | T_DOUBLE
+ | T_SIGNED
+ | T_UNSIGNED
+ | T_BOOL
+ | T_COMPLEX
+ | T_IMAGINARY
+
+typespec_tagged: T_STRUCT | T_UNION | T_ENUM
+
+qualifier_simple: T_CONST
+ | T_RESTRICT
+ | T_VOLATILE
declspec_notype: qualifier | declspec_simple {
ALLOC_STRUCT($$, struct cdecl_declspec, .type = $1);
ALLOC_STRUCT($$, struct cdecl_declspec, .type = $1);
}
-typespec: typespec_noid | T_STRUCT T_IDENT {
+typespec: typespec_noid | typespec_tagged T_IDENT {
ALLOC_STRUCT($$, struct cdecl_declspec,
- .type = CDECL_TYPE_STRUCT,
- .ident = $2);
-} | T_UNION T_IDENT {
- ALLOC_STRUCT($$, struct cdecl_declspec,
- .type = CDECL_TYPE_UNION,
- .ident = $2);
-} | T_ENUM T_IDENT {
- ALLOC_STRUCT($$, struct cdecl_declspec,
- .type = CDECL_TYPE_ENUM,
+ .type = $1,
.ident = $2);
} | T_IDENT {
ALLOC_STRUCT($$, struct cdecl_declspec,
array: T_LBRACKET T_UINT T_RBRACKET {
if ($2 == 0)
- FAIL("array length must be positive");
+ FAIL(_("array length must be positive"));
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
post_specs: qualifiers typespec type_qual_specs {
$$ = $2;
$$->next = $1;
- for (struct cdecl_declspec *s = $1; s; s = s->next) {
+ for (struct cdecl_declspec *s = $$; s; s = s->next) {
if (!s->next) {
s->next = $3;
break;
.u.array.vla = $3);
} | T_ARRAY T_UINT T_OF {
if ($2 == 0)
- FAIL("array length must be positive");
+ FAIL(_("array length must be positive"));
ALLOC_STRUCT($$, struct cdecl_declarator,
.type = CDECL_DECL_ARRAY,
}
%%
-void
-yyerror(YYLTYPE *loc, yyscan_t scanner, struct cdecl **out,
- const char *err)
-{
- if (strstr(err, "T_LEX_ERROR"))
- return;
- fprintf(stderr, "%s\n", err);
+/*
+ * Expose the token string table to the rest of the library, in order to
+ * produce strings that match parser keywords.
+ *
+ * In order for this to work properly, the Bison output must be postprocessed
+ * by fix-yytname.awk to remove pointless quotation marks from the keyword
+ * strings.
+ */
+const char *cdecl__token_name(unsigned token)
+{
+ return yytname[YYTRANSLATE(token)];
}