%{
#define YYDEBUG 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "parse.h"
int yyerror(char *);
int yylex(void);
extern char *filename, *original_filename;
%}
%union {
int token;
char *sval;
struct strloc strloc;
struct strloc_list *strloc_list;
}
/* keywords: control flow */
%token CZ_BREAK CZ_CASE CZ_CONTINUE CZ_DEFAULT CZ_DO CZ_ELSE CZ_FOR CZ_GOTO
%token CZ_IF CZ_RETURN CZ_SWITCH CZ_WHILE
/* keywords: basic types */
%token CZ_CHAR CZ_ENUM CZ_DOUBLE CZ_FLOAT CZ_INT CZ_LONG CZ_SHORT CZ_STRUCT
%token CZ_TYPEDEF CZ_UNION CZ_VOID
/* keywords: type qualifiers */
%token CZ_AUTO CZ_CONST CZ_EXTERN CZ_INLINE CZ_REGISTER CZ_RESTRICT CZ_SIGNED
%token CZ_STATIC CZ_UNSIGNED CZ_VOLATILE
/* keywords: misc */
%token CZ_SIZEOF
%token CZ_ALIGNAS CZ_ALIGNOF CZ_ATOMIC CZ_BOOL CZ_COMPLEX CZ_GENERIC
%token CZ_IMAGINARY CZ_NORETURN CZ_STATIC_ASSERT CZ_THREAD_LOCAL
/* punctuators
* note: single-character tokens use their ascii equivalent, and diagraphs are
* translated into their normalized equivalent by the lexer.
*/
%token CZ_PTR_DEREF CZ_INCREMENT CZ_DECREMENT CZ_LSHIFT CZ_RSHIFT
%token CZ_GTR_EQL CZ_LESS_EQL CZ_EQUAL CZ_NEQUAL CZ_LAND CZ_LOR
%token CZ_MUL_ASSIGN CZ_DIV_ASSIGN CZ_MOD_ASSIGN CZ_ADD_ASSIGN CZ_SUB_ASSIGN
%token CZ_LSHIFT_ASSIGN CZ_RSHIFT_ASSIGN
%token CZ_BAND_ASSIGN CZ_BXOR_ASSIGN CZ_BOR_ASSIGN
/* other tokens */
%token<strloc> CZ_IDENTIFIER
%token CZ_CONSTANT
%token<sval> CZ_STRING_LITERAL
%token CZ_ELLIPSIS
/* C O N T E X T */
%token TYPEDEF_NAME ENUMERATION_CONSTANT
/* tokens for reverse parsing */
%token CZ_ONELINE_COMMENT CZ_MULTILINE_COMMENT CZ_WHITESPACE
/* implementation details */
%token CZ_BUILTIN_VA_LIST CZ_BUILTIN_VA_ARG CZ_BUILTIN_VA_COPY
/* GNU extensions (fuck you GNU) */
%token CZ_GNU_ATTRIBUTE CZ_GNU_ASM
%type<strloc> declarator base_declarator direct_declarator init_declarator;
%type<strloc> primary_expression postfix_expression;
%type<strloc_list> init_declarator_list;
%type<token> storage_class_specifier declaration_specifiers;
%type<sval> string_literal;
%start program
%%
program
: {
fprintf(stderr, "Error: empty source file\n");
}
| translation_unit
;
string_literal
: CZ_STRING_LITERAL
| string_literal CZ_STRING_LITERAL {
$$ = calloc(strlen($1) + strlen($2) + 1, 1);
strcpy($$, $1);
strcat($$, $2);
}
;
primary_expression
: CZ_IDENTIFIER
| constant {
$$.name = NULL;
}
| string_literal {
$$.name = NULL;
}
| '(' expression ')' {
$$.name = NULL;
}
| generic_selection {
$$.name = NULL;
}
| CZ_BUILTIN_VA_ARG '(' CZ_IDENTIFIER ',' type_name ')' {
$$.name = NULL;
}
;
constant
: CZ_CONSTANT
| ENUMERATION_CONSTANT
;
enumeration_constant
: CZ_IDENTIFIER
;
generic_selection
: CZ_GENERIC '(' assignment_expression ',' generic_assoc_list ')'
;
generic_assoc_list
: generic_association
| generic_assoc_list ',' generic_association
;
generic_association
: type_name ':' assignment_expression
| CZ_DEFAULT ':' assignment_expression
;
postfix_expression
: primary_expression
| postfix_expression '[' expression ']'
| postfix_expression '(' ')' {
if ($1.name && strcmp(filename, original_filename) == 0) {
reference_symbol(&graph, current_source,
$1.name, $1.lineno, $1.colno);
}
}
| postfix_expression '(' argument_expression_list ')' {
if ($$.name && strcmp(filename, original_filename) == 0) {
reference_symbol(&graph, current_source,
$1.name, $1.lineno, $1.colno);
}
}
| postfix_expression '.' CZ_IDENTIFIER
| postfix_expression CZ_PTR_DEREF CZ_IDENTIFIER
| postfix_expression CZ_INCREMENT
| postfix_expression CZ_DECREMENT
| '(' type_name ')' '{' initializer_list '}' {
$$.name = NULL;
}
| '(' type_name ')' '{' initializer_list ',' '}' {
$$.name = NULL;
}
;
argument_expression_list
: assignment_expression
| argument_expression_list ',' assignment_expression
;
unary_expression
: postfix_expression
| CZ_INCREMENT unary_expression
| CZ_DECREMENT unary_expression
| unary_operator cast_expression
| CZ_SIZEOF unary_expression
| CZ_SIZEOF '(' type_name ')'
| CZ_ALIGNOF '(' type_name ')'
;
unary_operator
: '&'
| '*'
| '+'
| '-'
| '~'
| '!'
;
cast_expression
: unary_expression
| '(' type_name ')' cast_expression
;
multiplicative_expression
: cast_expression
| multiplicative_expression '*' cast_expression
| multiplicative_expression '/' cast_expression
| multiplicative_expression '%' cast_expression
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression
| additive_expression '-' multiplicative_expression
;
shift_expression
: additive_expression
| shift_expression CZ_LSHIFT additive_expression
| shift_expression CZ_RSHIFT additive_expression
;
relational_expression
: shift_expression
| relational_expression '<' shift_expression
| relational_expression '>' shift_expression
| relational_expression CZ_LESS_EQL shift_expression
| relational_expression CZ_GTR_EQL shift_expression
;
equality_expression
: relational_expression
| equality_expression CZ_EQUAL relational_expression
| equality_expression CZ_NEQUAL relational_expression
;
and_expression
: equality_expression
| and_expression '&' equality_expression
;
exclusive_or_expression
: and_expression
| exclusive_or_expression '^' and_expression
;
inclusive_or_expression
: exclusive_or_expression
| inclusive_or_expression '|' exclusive_or_expression
;
logical_and_expression
: inclusive_or_expression
| logical_and_expression CZ_LAND inclusive_or_expression
;
logical_or_expression
: logical_and_expression
| logical_or_expression CZ_LOR logical_and_expression
;
conditional_expression
: logical_or_expression
| logical_or_expression '?' expression ':' conditional_expression
;
assignment_expression
: conditional_expression
| unary_expression assignment_operator assignment_expression
;
assignment_operator
: '='
| CZ_MUL_ASSIGN
| CZ_DIV_ASSIGN
| CZ_MOD_ASSIGN
| CZ_ADD_ASSIGN
| CZ_SUB_ASSIGN
| CZ_LSHIFT_ASSIGN
| CZ_RSHIFT_ASSIGN
| CZ_BAND_ASSIGN
| CZ_BXOR_ASSIGN
| CZ_BOR_ASSIGN
;
expression
: assignment_expression
| expression ',' assignment_expression
| gnu_inline_asm_expression
;
constant_expression
: conditional_expression /* with constraints */
;
declaration
: declaration_specifiers ';'
| declaration_specifiers init_declarator_list ';' {
if ($1 == CZ_TYPEDEF) {
struct strloc_list *list = $2, *next;
while (list) {
struct typedef_declaration *td =
calloc(1, sizeof(struct typedef_declaration));
td->name = list->strloc.name;
td->lineno = list->strloc.lineno;
td->colno = list->strloc.colno;
td->next = current_source->typedefs;
current_source->typedefs = td;
next = list->next;
free(list);
list = next;
}
}
}
| static_assert_declaration
;
declaration_specifiers
: storage_class_specifier declaration_specifiers
| storage_class_specifier
| type_specifier declaration_specifiers { $$ = 0; }
| type_specifier { $$ = 0; }
| type_qualifier declaration_specifiers { $$ = 0; }
| type_qualifier { $$ = 0; }
| function_specifier declaration_specifiers { $$ = 0; }
| function_specifier { $$ = 0; }
| alignment_specifier declaration_specifiers { $$ = 0; }
| alignment_specifier { $$ = 0; }
| gnu_attribute_specifiers declaration_specifiers { $$ = 0; }
| gnu_attribute_specifiers { $$ = 0; }
;
init_declarator_list
: init_declarator {
$$ = calloc(1, sizeof(struct strloc_list));
$$->strloc = $1;
}
| init_declarator_list ',' init_declarator {
$$ = calloc(1, sizeof(struct strloc_list));
$$->next = $1;
$$->strloc = $3;
}
;
init_declarator
: declarator '=' initializer
| declarator
;
storage_class_specifier
: CZ_TYPEDEF { $$ = CZ_TYPEDEF; }
| CZ_EXTERN { $$ = CZ_EXTERN; }
| CZ_STATIC { $$ = CZ_STATIC; }
| CZ_THREAD_LOCAL { $$ = CZ_THREAD_LOCAL; }
| CZ_AUTO { $$ = CZ_AUTO; }
| CZ_REGISTER { $$ = CZ_REGISTER; }
;
type_specifier
: CZ_VOID
| CZ_CHAR
| CZ_SHORT
| CZ_INT
| CZ_LONG
| CZ_FLOAT
| CZ_DOUBLE
| CZ_SIGNED
| CZ_UNSIGNED
| CZ_BOOL
| CZ_COMPLEX
| CZ_IMAGINARY
| CZ_BUILTIN_VA_LIST
| atomic_type_specifier
| struct_or_union_specifier
| enum_specifier
| TYPEDEF_NAME
;
struct_or_union_specifier
: struct_or_union '{' struct_declaration_list '}'
| struct_or_union CZ_IDENTIFIER '{' struct_declaration_list '}'
| struct_or_union CZ_IDENTIFIER
;
struct_or_union
: CZ_STRUCT
| CZ_UNION
;
struct_declaration_list
: struct_declaration
| struct_declaration_list struct_declaration
;
struct_declaration
: specifier_qualifier_list ';' /* for anonymous struct/union */
| specifier_qualifier_list struct_declarator_list ';'
| static_assert_declaration
;
specifier_qualifier_list
: type_specifier specifier_qualifier_list
| type_specifier
| type_qualifier specifier_qualifier_list
| type_qualifier
;
struct_declarator_list
: struct_declarator
| struct_declarator_list ',' struct_declarator
;
struct_declarator
: ':' constant_expression
| declarator ':' constant_expression
| declarator
;
enum_specifier
: CZ_ENUM '{' enumerator_list '}'
| CZ_ENUM '{' enumerator_list ',' '}'
| CZ_ENUM CZ_IDENTIFIER '{' enumerator_list '}'
| CZ_ENUM CZ_IDENTIFIER '{' enumerator_list ',' '}'
| CZ_ENUM CZ_IDENTIFIER
;
enumerator_list
: enumerator
| enumerator_list ',' enumerator
;
enumerator /* identifiers must be flagged as ENUMERATION_CONSTANT */
: enumeration_constant '=' constant_expression
| enumeration_constant
;
atomic_type_specifier
: CZ_ATOMIC '(' type_name ')'
;
type_qualifier
: CZ_CONST
| CZ_RESTRICT
| CZ_VOLATILE
| CZ_ATOMIC
;
function_specifier
: CZ_INLINE
| CZ_NORETURN
;
alignment_specifier
: CZ_ALIGNAS '(' type_name ')'
| CZ_ALIGNAS '(' constant_expression ')'
;
declarator
: base_declarator
| base_declarator gnu_inline_asm_expression
| base_declarator gnu_attribute_specifiers
| base_declarator gnu_inline_asm_expression gnu_attribute_specifiers
| base_declarator gnu_attribute_specifiers gnu_inline_asm_expression
| gnu_attribute_specifiers base_declarator { $$ = $2; }
| gnu_attribute_specifiers base_declarator gnu_inline_asm_expression {
$$ = $2;
}
;
base_declarator
: pointer direct_declarator {
$$ = $2;
}
| direct_declarator
;
direct_declarator
: CZ_IDENTIFIER
| '(' declarator ')' {
$$ = $2;
}
| direct_declarator '[' ']'
| direct_declarator '[' '*' ']'
| direct_declarator '[' CZ_STATIC type_qualifier_list assignment_expression ']'
| direct_declarator '[' CZ_STATIC assignment_expression ']'
| direct_declarator '[' type_qualifier_list '*' ']'
| direct_declarator '[' type_qualifier_list CZ_STATIC assignment_expression ']'
| direct_declarator '[' type_qualifier_list assignment_expression ']'
| direct_declarator '[' type_qualifier_list ']'
| direct_declarator '[' assignment_expression ']'
| direct_declarator '(' parameter_type_list ')'
| direct_declarator '(' ')'
| direct_declarator '(' identifier_list ')'
;
pointer
: '*' type_qualifier_list pointer
| '*' type_qualifier_list
| '*' pointer
| '*'
| '*' gnu_attribute_specifiers type_qualifier_list pointer
| '*' gnu_attribute_specifiers pointer
| '*' gnu_attribute_specifiers
;
type_qualifier_list
: type_qualifier
| type_qualifier_list type_qualifier
;
parameter_type_list
: parameter_list ',' CZ_ELLIPSIS
| parameter_list
;
parameter_list
: parameter_declaration
| parameter_list ',' parameter_declaration
;
parameter_declaration
: declaration_specifiers declarator
| declaration_specifiers abstract_declarator
| declaration_specifiers
;
identifier_list
: CZ_IDENTIFIER
| identifier_list ',' CZ_IDENTIFIER
;
type_name
: specifier_qualifier_list abstract_declarator
| specifier_qualifier_list
;
abstract_declarator
: pointer direct_abstract_declarator
| pointer
| direct_abstract_declarator
;
direct_abstract_declarator
: '(' abstract_declarator ')'
| '[' ']'
| '[' '*' ']'
| '[' CZ_STATIC type_qualifier_list assignment_expression ']'
| '[' CZ_STATIC assignment_expression ']'
| '[' type_qualifier_list CZ_STATIC assignment_expression ']'
| '[' type_qualifier_list assignment_expression ']'
| '[' type_qualifier_list ']'
| '[' assignment_expression ']'
| direct_abstract_declarator '[' ']'
| direct_abstract_declarator '[' '*' ']'
| direct_abstract_declarator '[' CZ_STATIC type_qualifier_list assignment_expression ']'
| direct_abstract_declarator '[' CZ_STATIC assignment_expression ']'
| direct_abstract_declarator '[' type_qualifier_list assignment_expression ']'
| direct_abstract_declarator '[' type_qualifier_list CZ_STATIC assignment_expression ']'
| direct_abstract_declarator '[' type_qualifier_list ']'
| direct_abstract_declarator '[' assignment_expression ']'
| '(' ')'
| '(' parameter_type_list ')'
| direct_abstract_declarator '(' ')'
| direct_abstract_declarator '(' parameter_type_list ')'
;
initializer
: '{' initializer_list '}'
| '{' initializer_list ',' '}'
| assignment_expression
;
initializer_list
: designation initializer
| initializer
| initializer_list ',' designation initializer
| initializer_list ',' initializer
;
designation
: designator_list '='
;
designator_list
: designator
| designator_list designator
;
designator
: '[' constant_expression ']'
| '.' CZ_IDENTIFIER
;
static_assert_declaration
: CZ_STATIC_ASSERT '(' constant_expression ',' string_literal ')' ';'
;
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
labeled_statement
: CZ_IDENTIFIER ':' statement
| CZ_CASE constant_expression ':' statement
| CZ_DEFAULT ':' statement
;
compound_statement
: '{' '}'
| '{' block_item_list '}'
;
block_item_list
: block_item
| block_item_list block_item
;
block_item
: declaration
| statement
;
expression_statement
: ';'
| expression ';'
;
selection_statement
: CZ_IF '(' expression ')' statement CZ_ELSE statement
| CZ_IF '(' expression ')' statement
| CZ_SWITCH '(' expression ')' statement
;
iteration_statement
: CZ_WHILE '(' expression ')' statement
| CZ_DO statement CZ_WHILE '(' expression ')' ';'
| CZ_FOR '(' expression_statement expression_statement ')' statement
| CZ_FOR '(' expression_statement expression_statement expression ')' statement
| CZ_FOR '(' declaration expression_statement ')' statement
| CZ_FOR '(' declaration expression_statement expression ')' statement
;
jump_statement
: CZ_GOTO CZ_IDENTIFIER ';'
| CZ_CONTINUE ';'
| CZ_BREAK ';'
| CZ_RETURN ';'
| CZ_RETURN expression ';'
;
translation_unit
: external_declaration
| translation_unit external_declaration
;
external_declaration
: function_definition
| declaration
;
function_definition
: declaration_specifiers declarator declaration_list compound_statement {
if (!filename || strcmp(filename, original_filename) == 0) {
define_symbol(&graph, current_source, $2.name,
$1 == CZ_STATIC, $2.lineno, $2.colno);
}
}
| declaration_specifiers declarator compound_statement {
if (!filename || strcmp(filename, original_filename) == 0) {
define_symbol(&graph, current_source, $2.name,
$1 == CZ_STATIC, $2.lineno, $2.colno);
}
}
;
declaration_list
: declaration
| declaration_list declaration
;
gnu_attribute_specifiers /* GNU sucks */
: CZ_GNU_ATTRIBUTE '(' '(' ')' ')'
| CZ_GNU_ATTRIBUTE '(' '(' gnu_attribute_list ')' ')'
| gnu_attribute_specifiers gnu_attribute_specifiers
;
gnu_attribute_specifier
: postfix_expression
| type_specifier
;
gnu_attribute_list
: gnu_attribute_specifier
| gnu_attribute_list ',' gnu_attribute_specifier
;
gnu_inline_asm_expression
: CZ_GNU_ASM '(' gnu_inline_asm ')'
| CZ_GNU_ASM gnu_asm_qualifiers '(' gnu_inline_asm ')'
;
gnu_asm_qualifiers
: CZ_VOLATILE
| CZ_INLINE
| CZ_GOTO
;
gnu_inline_asm
: string_literal
| string_literal ':'
| string_literal ':' gnu_asm_output_operands
| string_literal ':' gnu_asm_output_operands ':'
| string_literal ':' gnu_asm_output_operands ':' gnu_asm_input_operands ':' gnu_asm_clobbers
| string_literal ':' gnu_asm_output_operands ':' gnu_asm_input_operands ':' gnu_asm_clobbers ':'
| string_literal ':' gnu_asm_output_operands ':' gnu_asm_input_operands ':' gnu_asm_clobbers ':' gnu_asm_goto_labels
;
gnu_asm_output_operand
: string_literal '(' CZ_IDENTIFIER ')'
;
gnu_asm_output_operands
: gnu_asm_output_operand
| gnu_asm_output_operands ',' gnu_asm_output_operand
;
gnu_asm_input_operand
: string_literal '(' expression ')'
;
gnu_asm_input_operands
: gnu_asm_input_operand
| gnu_asm_input_operands ',' gnu_asm_input_operand
;
gnu_asm_clobbers
: string_literal
| gnu_asm_clobbers ',' string_literal
;
gnu_asm_goto_labels
: CZ_IDENTIFIER
| gnu_asm_goto_labels ',' CZ_IDENTIFIER
;