C99

/* $Id$ */
/* Copyright (c) 2008-2015 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Devel c99 */
/* 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
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* FIXME:
* - check calls to code_context_set() */
#include <assert.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "common.h"
#include "tokenset.h"
#include "scanner.h"
#include "parser.h"
#include "../config.h"
#ifdef DEBUG
# define DEBUG_GRAMMAR() fprintf(stderr, "DEBUG: %s(%p) \"%s\"\n", __func__, \
(void *)c99, _parse_get_string(c99))
#else
# define DEBUG_GRAMMAR()
#endif
/* private */
/* prototypes */
static int _parse_check(C99 * c99, C99Code code);
static int _parse_check_set(C99 * c99, TokenSet set, char const * name,
int (*callback)(C99 * c99));
static int _parse_error(C99 * c99, char const * format, ...);
static int _parse_get_code(C99 * c99);
static char const * _parse_get_string(C99 * c99);
static int _parse_in_set(C99 * c99, TokenSet set);
static int _parse_is_code(C99 * c99, C99Code code);
static int _parse_skip(C99 * c99, TokenSet set);
/* grammar */
static int _translation_unit(C99 * c99);
static int _external_declaration(C99 * c99);
static int _function_definition(C99 * c99);
static int _declaration_specifiers(C99 * c99);
static int _storage_class_specifier(C99 * c99);
static int _type_specifier(C99 * c99);
static int _struct_or_union_specifier(C99 * c99);
static int _struct_or_union(C99 * c99);
static int _struct_declaration_list(C99 * c99);
static int _struct_declaration(C99 * c99);
static int _struct_declarator_list(C99 * c99);
static int _struct_declarator(C99 * c99);
static int _enum_specifier(C99 * c99);
static int _enumerator(C99 * c99);
static int _enumeration_constant(C99 * c99);
static int _typedef_name(C99 * c99);
static int _type_qualifier(C99 * c99);
static int _function_specifier(C99 * c99);
static int _declarator(C99 * c99);
static int _pointer(C99 * c99);
static int _type_qualifier_list(C99 * c99);
static int _direct_declarator(C99 * c99);
static int _identifier(C99 * c99);
static int _identifier_list(C99 * c99);
static int _parameter_type_list(C99 * c99);
static int _parameter_declaration(C99 * c99);
static int _abstract_or_declarator(C99 * c99);
static int _abstract_declarator(C99 * c99);
static int _direct_abstract_declarator(C99 * c99);
static int _assignment_expr(C99 * c99);
static int _unary_expr(C99 * c99);
static int _postfix_expr(C99 * c99);
static int _postfix_expr_do(C99 * c99);
static int _argument_expr_list(C99 * c99);
static int _primary_expr(C99 * c99);
static int _type_name(C99 * c99);
static int _specifier_qualifier_list(C99 * c99);
static int _unary_operator(C99 * c99);
static int _assignment_operator(C99 * c99);
static int _conditional_expr(C99 * c99);
static int _logical_or_expr(C99 * c99);
static int _logical_and_expr(C99 * c99);
static int _inclusive_or_expr(C99 * c99);
static int _exclusive_or_expr(C99 * c99);
static int _and_expr(C99 * c99);
static int _equality_expr(C99 * c99);
static int _relational_expr(C99 * c99);
static int _shift_expr(C99 * c99);
static int _additive_expr(C99 * c99);
static int _multiplicative_expr(C99 * c99);
static int _cast_expr(C99 * c99);
static int _declaration_list(C99 * c99);
static int _declaration(C99 * c99);
static int _declaration_do(C99 * c99);
static int _compound_statement(C99 * c99);
static int _block_item_list(C99 * c99);
static int _block_item(C99 * c99);
static int _statement(C99 * c99);
static int _labeled_statement(C99 * c99);
static int _constant_expr(C99 * c99);
static int _expression_statement(C99 * c99);
static int _expression(C99 * c99);
static int _selection_statement(C99 * c99);
static int _iteration_statement(C99 * c99);
static int _jump_statement(C99 * c99);
static int _init_declarator_list(C99 * c99);
static int _init_declarator(C99 * c99);
static int _initializer(C99 * c99);
static int _initializer_list(C99 * c99);
static int _designation(C99 * c99);
static int _designator_list(C99 * c99);
static int _designator(C99 * c99);
/* functions */
/* parse_check */
static int _parse_check(C99 * c99, C99Code code)
{
int ret = 0;
if(!_parse_is_code(c99, code))
{
ret = _parse_error(c99, "Expected \"%s\"",
tokencode_get_string(code));
/* FIXME is it really good? limit to 3 scans? search ; or } ? */
while(scan(c99) == 0 && c99->token != NULL /* actual token */
&& !_parse_is_code(c99, code));
}
ret |= scan(c99);
return ret;
}
/* parse_check_set */
static int _parse_check_set(C99 * c99, TokenSet set, char const * name,
int (*callback)(C99 * c99))
{
if(!_parse_in_set(c99, set))
return _parse_error(c99, "Expected %s", name);
return callback(c99);
}
/* parse_error */
static int _parse_error(C99 * c99, char const * format, ...)
{
Token * token = c99->token;
va_list ap;
c99->error_cnt++;
if(token == NULL) /* XXX not very elegant */
fputs(PACKAGE ": near end of file: error: ", stderr);
else
fprintf(stderr, "%s%s:%u, near \"%s\": error: ", PACKAGE ": ",
token_get_filename(token),
token_get_line(token), token_get_string(token));
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fputc('\n', stderr);
return 1;
}
/* parse_get_code */
static int _parse_get_code(C99 * c99)
{
if(c99->token == NULL)
return TC_NULL;
return token_get_code(c99->token);
}
/* parse_get_string */
static char const * _parse_get_string(C99 * c99)
{
if(c99->token == NULL)
return "EOF";
return token_get_string(c99->token);
}
/* parse_in_set */
static int _parse_in_set(C99 * c99, TokenSet set)
{
if(c99->token == NULL)
return 0;
return token_in_set(c99->token, set);
}
/* parse_is_code */
static int _parse_is_code(C99 * c99, C99Code code)
{
if(c99->token == NULL)
return 0;
return token_get_code(c99->token) == code;
}
/* parse_skip */
static int _parse_skip(C99 * c99, TokenSet set)
{
int ret = 0;
while(!_parse_in_set(c99, set) && (ret = scan(c99)) == 0
&& c99->token != NULL);
return ret;
}
/* grammar */
/* translation-unit */
static int _translation_unit(C99 * c99)
/* external-declaration { external-declaration } */
{
int ret = 0;
DEBUG_GRAMMAR();
if(scan(c99) != 0)
return 1;
while(c99->token != NULL) /* end of file */
if(!_parse_in_set(c99, c99set_external_declaration))
{
ret |= _parse_error(c99, "Expected declaration");
ret |= _parse_skip(c99, c99set_external_declaration);
}
else
ret |= _external_declaration(c99);
if(c99->token != NULL)
ret |= 1;
return ret;
}
/* external_declaration
* PRE the first token starts a declaration-specifiers */
static int _external_declaration(C99 * c99)
/* function-definition | declaration */
{
int ret;
DEBUG_GRAMMAR();
code_context_set(c99->code, CODE_CONTEXT_DECLARATION_OR_FUNCTION);
ret = _declaration_specifiers(c99);
if(_parse_is_code(c99, C99_CODE_OPERATOR_SEMICOLON))
{
code_context_set(c99->code, CODE_CONTEXT_DECLARATION);
ret |= _declaration_do(c99);
return ret;
}
ret |= _declarator(c99);
if(_parse_is_code(c99, C99_CODE_OPERATOR_EQUALS))
{
code_context_set(c99->code, CODE_CONTEXT_DECLARATION);
ret |= scan(c99);
ret |= _initializer(c99);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
ret |= _init_declarator(c99);
}
ret |= _declaration_do(c99);
return ret;
}
if(_parse_in_set(c99, c99set_declaration_list))
{
code_context_set(c99->code, CODE_CONTEXT_FUNCTION);
ret |= _declaration_list(c99);
ret |= _parse_check_set(c99, c99set_compound_statement,
"function definition", _function_definition);
return ret;
}
if(_parse_in_set(c99, c99set_compound_statement))
{
code_context_set(c99->code, CODE_CONTEXT_FUNCTION);
ret |= _function_definition(c99);
return ret;
}
code_context_set(c99->code, CODE_CONTEXT_DECLARATION);
if(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
ret |= _init_declarator_list(c99);
}
ret |= _declaration_do(c99);
return ret;
}
/* function_definition
* PRE the first token starts a compound-statement */
static int _function_definition(C99 * c99)
/* compound-statement */
{
int ret = 0;
DEBUG_GRAMMAR();
code_context_set(c99->code, CODE_CONTEXT_FUNCTION_BEGIN);
ret |= _compound_statement(c99);
code_context_set(c99->code, CODE_CONTEXT_FUNCTION_END);
return ret;
}
/* declaration-list */
static int _declaration_list(C99 * c99)
/* declaration { declaration } */
{
int ret;
DEBUG_GRAMMAR();
ret = _declaration(c99);
while(_parse_in_set(c99, c99set_declaration))
ret |= _declaration(c99);
return ret;
}
/* declaration */
static int _declaration(C99 * c99)
/* declaration-specifiers [ init-declarator-list ] ";" */
{
int ret = 0;
DEBUG_GRAMMAR();
ret |= _declaration_specifiers(c99);
if(_parse_in_set(c99, c99set_init_declarator_list))
ret |= _init_declarator_list(c99);
ret |= _declaration_do(c99);
return ret;
}
/* PRE a full declaration was just parsed until ";" */
static int _declaration_do(C99 * c99)
{
int ret;
ret = _parse_check(c99, C99_CODE_OPERATOR_SEMICOLON);
ret |= code_context_set(c99->code, CODE_CONTEXT_DECLARATION_END);
return ret;
}
/* declaration-specifiers */
static int _declaration_specifiers(C99 * c99)
/* storage-class-specifier [ declaration-specifiers ]
* type-specifier [ declaration-specifiers ]
* type-qualifier [ declaration-specifiers ]
* function-specifier [ declaration-specifiers ] */
{
int ret = 0;
int looped;
DEBUG_GRAMMAR();
for(looped = 0;; looped = 1)
if(_parse_in_set(c99, c99set_storage_class_specifier))
ret |= _storage_class_specifier(c99);
else if(_parse_in_set(c99, c99set_type_specifier))
ret |= _type_specifier(c99);
else if(_parse_in_set(c99, c99set_type_qualifier))
ret |= _type_qualifier(c99);
else if(_parse_in_set(c99, c99set_function_specifier))
ret |= _function_specifier(c99);
else
{
if(looped == 0)
ret |= _parse_error(c99, "Expected declaration"
" specifier");
break;
}
return ret;
}
/* storage-class-specifier
* PRE the first token starts a storage-class-specifier */
static int _storage_class_specifier(C99 * c99)
/* "typedef" | "extern" | "static" | "auto" | "register" */
{
int ret;
CodeStorage storage = CODE_STORAGE_NULL;
DEBUG_GRAMMAR();
switch(_parse_get_code(c99))
{
case C99_CODE_KEYWORD_TYPEDEF:
storage = CODE_STORAGE_TYPEDEF;
break;
case C99_CODE_KEYWORD_EXTERN:
storage = CODE_STORAGE_EXTERN;
break;
case C99_CODE_KEYWORD_STATIC:
storage = CODE_STORAGE_STATIC;
break;
case C99_CODE_KEYWORD_AUTO:
storage = CODE_STORAGE_AUTO;
break;
case C99_CODE_KEYWORD_REGISTER:
storage = CODE_STORAGE_REGISTER;
break;
default:
break;
}
if((ret = code_context_set_storage(c99->code, storage)) != 0)
_parse_error(c99, error_get(NULL));
ret |= scan(c99);
return ret;
}
/* type-specifier
* PRE the first token starts a storage-class-specifier */
static int _type_specifier(C99 * c99)
/* void | char | short | int | long | float | double | signed | unsigned
* | _Bool | _Complex | _Imaginary | struct-or-union-specifier
* | enum-specifier | typedef-name */
{
int ret = 0;
CodeClass cclass = CODE_CLASS_NULL;
DEBUG_GRAMMAR();
if(_parse_in_set(c99, c99set_struct_or_union_specifier))
return _struct_or_union_specifier(c99);
else if(_parse_in_set(c99, c99set_enum_specifier))
return _enum_specifier(c99);
else if(_parse_in_set(c99, c99set_typedef_name))
return _typedef_name(c99);
switch(_parse_get_code(c99))
{
case C99_CODE_KEYWORD_VOID:
cclass = CODE_CLASS_VOID;
break;
case C99_CODE_KEYWORD_CHAR:
cclass = CODE_CLASS_CHAR;
break;
case C99_CODE_KEYWORD_SHORT:
cclass = CODE_CLASS_SHORT;
break;
case C99_CODE_KEYWORD_INT:
cclass = CODE_CLASS_INT;
break;
case C99_CODE_KEYWORD_LONG:
cclass = CODE_CLASS_LONG;
break;
case C99_CODE_KEYWORD_FLOAT:
cclass = CODE_CLASS_FLOAT;
break;
case C99_CODE_KEYWORD_DOUBLE:
cclass = CODE_CLASS_DOUBLE;
break;
case C99_CODE_KEYWORD_SIGNED:
cclass = CODE_CLASS_SIGNED;
break;
case C99_CODE_KEYWORD_UNSIGNED:
cclass = CODE_CLASS_UNSIGNED;
break;
case C99_CODE_KEYWORD__BOOL:
cclass = CODE_CLASS__BOOL;
break;
case C99_CODE_KEYWORD__COMPLEX:
cclass = CODE_CLASS__COMPLEX;
break;
case C99_CODE_KEYWORD__IMAGINARY:
cclass = CODE_CLASS__IMAGINARY;
break;
default:
assert(0);
break;
}
if((ret = code_context_set_class(c99->code, cclass)) != 0)
_parse_error(c99, error_get(NULL));
ret |= scan(c99);
return ret;
}
/* struct-or-union-specifier
* PRE the first token starts a struct-or-union-specifier */
static int _struct_or_union_specifier(C99 * c99)
/* struct-or-union [ identifier ] "{" struct-declaration-list "}"
* | struct-or-union identifier */
{
int ret;
CodeContext context;
C99Code code;
DEBUG_GRAMMAR();
context = code_context_get(c99->code);
ret = _struct_or_union(c99);
if((code = _parse_get_code(c99)) == C99_CODE_IDENTIFIER)
{
ret |= _identifier(c99);
if(!_parse_is_code(c99, C99_CODE_OPERATOR_LBRACE))
{
ret |= code_context_set(c99->code, context);
return ret;
}
}
else if(code != C99_CODE_OPERATOR_LBRACE)
{
ret |= code_context_set(c99->code, context);
ret |= _parse_error(c99, "%s", "Expected identifier or {");
return ret;
}
ret |= scan(c99);
/* XXX the grammar says there's got to be one */
if(_parse_in_set(c99, c99set_struct_declaration_list))
{
ret |= _struct_declaration_list(c99);
/* XXX the grammar doesn't mention this? */
if(_parse_is_code(c99, C99_CODE_COMMA))
ret |= scan(c99);
}
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACE);
ret |= code_context_set(c99->code, context);
return ret;
}
/* struct-or-union
* PRE the first token starts a struct-or-union */
static int _struct_or_union(C99 * c99)
/* "struct" | "union" */
{
int ret;
DEBUG_GRAMMAR();
ret = code_context_set(c99->code, _parse_get_code(c99)
== C99_CODE_KEYWORD_STRUCT ? CODE_CONTEXT_STRUCT
: CODE_CONTEXT_UNION);
ret |= scan(c99);
return ret;
}
/* struct-declaration-list
* PRE the first token starts a struct-declaration-list */
static int _struct_declaration_list(C99 * c99)
/* struct-declaration { struct-declaration } */
{
int ret;
DEBUG_GRAMMAR();
ret = _struct_declaration(c99);
while(_parse_in_set(c99, c99set_struct_declaration))
ret |= _struct_declaration(c99);
return ret;
}
/* struct-declaration
* PRE the first token starts a struct-declaration */
static int _struct_declaration(C99 * c99)
/* specifier-qualifier-list struct-declarator-list ";" */
{
int ret;
DEBUG_GRAMMAR();
ret = _specifier_qualifier_list(c99);
ret |= _struct_declarator_list(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_SEMICOLON);
return ret;
}
/* struct-declarator-list */
static int _struct_declarator_list(C99 * c99)
/* struct-declarator { "," struct-declarator } */
{
int ret;
DEBUG_GRAMMAR();
ret = _struct_declarator(c99);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
ret |= _struct_declarator(c99);
}
return ret;
}
/* struct-declarator */
static int _struct_declarator(C99 * c99)
/* declarator
* [ declarator ] ":" constant-expr */
{
int ret = 0;
DEBUG_GRAMMAR();
if(!_parse_is_code(c99, C99_CODE_OPERATOR_COLON))
ret |= _declarator(c99);
if(_parse_is_code(c99, C99_CODE_OPERATOR_COLON))
{
ret |= scan(c99);
ret |= _constant_expr(c99);
}
return ret;
}
/* enum-specifier
* PRE the first token starts an enum-specifier */
static int _enum_specifier(C99 * c99)
/* "enum" [ identifier ] "{" enumerator { "," enumerator } [ ","] "}"
* "enum" identifier */
{
int ret;
CodeContext context;
DEBUG_GRAMMAR();
context = code_context_get(c99->code);
ret = scan(c99);
if(_parse_is_code(c99, C99_CODE_IDENTIFIER))
{
code_context_set(c99->code, CODE_CONTEXT_ENUMERATION_NAME);
ret |= _identifier(c99);
if(!_parse_is_code(c99, C99_CODE_OPERATOR_LBRACE))
{
code_context_set(c99->code, context);
return ret;
}
}
ret |= _parse_check(c99, C99_CODE_OPERATOR_LBRACE);
ret |= _parse_check_set(c99, c99set_enumerator, "enumerator",
_enumerator);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
if(!_parse_is_code(c99, C99_CODE_OPERATOR_RBRACE))
ret |= _parse_check_set(c99, c99set_enumerator,
"enumerator", _enumerator);
else
break;
}
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACE);
code_context_set(c99->code, context);
return ret;
}
/* enumerator
* PRE the first token starts an enumerator */
static int _enumerator(C99 * c99)
/* enumeration-constant [ "=" constant-expression ] */
{
int ret;
DEBUG_GRAMMAR();
code_context_set(c99->code, CODE_CONTEXT_ENUMERATION_CONSTANT);
ret = _enumeration_constant(c99);
if(_parse_is_code(c99, C99_CODE_OPERATOR_EQUALS))
{
ret |= scan(c99);
code_context_set(c99->code, CODE_CONTEXT_ENUMERATION_VALUE);
ret |= _constant_expr(c99);
}
return ret;
}
/* enumeration-constant
* PRE the first token starts an enumeration-constant */
static int _enumeration_constant(C99 * c99)
/* identifier */
{
DEBUG_GRAMMAR();
return _identifier(c99);
}
/* typedef-name
* PRE the first token starts a typedef-name */
static int _typedef_name(C99 * c99)
/* identifier */
{
int ret;
CodeContext context;
DEBUG_GRAMMAR();
context = code_context_get(c99->code);
ret = code_context_set(c99->code, CODE_CONTEXT_TYPEDEF_NAME);
ret |= _identifier(c99);
ret |= code_context_set(c99->code, context);
return ret;
}
/* type-qualifier
* PRE the first token starts a type-qualifier */
static int _type_qualifier(C99 * c99)
/* "const" | "restrict" | "volatile" */
{
DEBUG_GRAMMAR();
return scan(c99);
}
/* function-specifier
* PRE the first token starts a function-specifier */
static int _function_specifier(C99 * c99)
/* "inline" */
{
DEBUG_GRAMMAR();
return scan(c99);
}
/* declarator */
static int _declarator(C99 * c99)
/* [ pointer ] direct-declarator */
{
int ret = 0;
DEBUG_GRAMMAR();
if(_parse_in_set(c99, c99set_pointer))
ret |= _pointer(c99);
ret |= _direct_declarator(c99);
return ret;
}
/* pointer
* PRE the first token starts a pointer */
static int _pointer(C99 * c99)
/* "*" [ type-qualifier-list ] { "*" [ type-qualifier-list ] } */
{
int ret;
DEBUG_GRAMMAR();
ret = scan(c99);
if(_parse_in_set(c99, c99set_type_qualifier_list))
ret |= _type_qualifier_list(c99);
while(_parse_in_set(c99, c99set_pointer))
{
ret |= scan(c99);
if(_parse_in_set(c99, c99set_type_qualifier_list))
ret |= _type_qualifier_list(c99);
}
return ret;
}
/* type-qualifier-list
* PRE the first token starts a type-qualifier-list */
static int _type_qualifier_list(C99 * c99)
/* type-qualifier { type-qualifier } */
{
int ret;
DEBUG_GRAMMAR();
ret = _type_qualifier(c99);
while(_parse_in_set(c99, c99set_type_qualifier))
ret |= _type_qualifier(c99);
return ret;
}
/* direct-declarator */
static int _direct_declarator(C99 * c99)
/* identifier
* "(" declarator ")"
* direct-declarator "[" [ assignment-expr | "*" ] "]"
* direct-declarator "(" parameter-type-list ")"
* direct-declarator "(" [ identifier-list ] ")" */
{
int ret;
C99Code code;
DEBUG_GRAMMAR();
if(_parse_is_code(c99, C99_CODE_OPERATOR_LPAREN))
{
ret = scan(c99);
ret |= _declarator(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
else
ret = _identifier(c99);
while(_parse_is_code(c99, C99_CODE_OPERATOR_LPAREN)
|| _parse_is_code(c99, C99_CODE_OPERATOR_LBRACKET))
{
code = token_get_code(c99->token);
ret |= scan(c99);
if(code == C99_CODE_OPERATOR_LBRACKET)
{
if(_parse_is_code(c99, C99_CODE_OPERATOR_TIMES))
ret |= scan(c99);
else if(_parse_in_set(c99, c99set_assignment_expr))
ret |= _assignment_expr(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACKET);
}
else /* C99_CODE_OPERATOR_LPAREN */
{
if(_parse_in_set(c99, c99set_parameter_type_list))
{
ret |= code_context_set(c99->code,
CODE_CONTEXT_PARAMETERS_TYPE);
ret |= _parameter_type_list(c99);
}
else if(_parse_in_set(c99, c99set_identifier_list))
{
ret |= code_context_set(c99->code,
CODE_CONTEXT_PARAMETERS);
ret |= _identifier_list(c99);
}
else
ret |= code_context_set(c99->code,
CODE_CONTEXT_PARAMETERS);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
}
return ret;
}
/* identifier */
static int _identifier(C99 * c99)
/* identifier-nondigit { (identifier-nondigit | identifier-digit) } */
{
int ret = 0;
DEBUG_GRAMMAR();
if(code_context_set_identifier(c99->code, _parse_get_string(c99)) != 0)
ret = _parse_error(c99, "%s", error_get(NULL));
ret |= scan(c99);
return ret;
}
/* identifier-list
* PRE the first token starts an identifier-list */
static int _identifier_list(C99 * c99)
/* identifier { "," identifier } */
{
int ret;
DEBUG_GRAMMAR();
ret = _identifier(c99);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
ret |= _parse_check_set(c99, c99set_identifier, "identifier",
_identifier);
}
return ret;
}
/* parameter-type-list
* PRE the first token starts a parameter-type-list */
static int _parameter_type_list(C99 * c99)
/* parameter-declaration { "," parameter-declaration } [ "," "..." ] */
{
int ret;
DEBUG_GRAMMAR();
ret = _parameter_declaration(c99);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
if(_parse_is_code(c99, C99_CODE_OPERATOR_DOTDOTDOT))
{
ret |= scan(c99);
break;
}
ret |= _parameter_declaration(c99);
}
return ret;
}
/* parameter-declaration */
static int _parameter_declaration(C99 * c99)
/* declaration-specifiers [ (declarator | abstract-declarator) ] */
{
int ret;
DEBUG_GRAMMAR();
ret = _declaration_specifiers(c99);
if(_parse_in_set(c99, c99set_abstract_or_declarator))
ret |= _abstract_or_declarator(c99);
return ret;
}
/* abstract-or-declarator */
static int _abstract_or_declarator(C99 * c99)
/* pointer
* [ pointer ] (direct-declarator | direct-abstract-declarator) */
{
int ret = 0;
DEBUG_GRAMMAR();
if(_parse_in_set(c99, c99set_pointer))
ret |= _pointer(c99);
if(_parse_is_code(c99, C99_CODE_IDENTIFIER))
ret |= _direct_declarator(c99);
else if(_parse_is_code(c99, C99_CODE_OPERATOR_LPAREN))
/* after "(" it can be:
* - declarator (pointer, identifier, "(")
* - abstract-declarator (pointer, parameter-type-list, "(",
* "[", nothing) */
while(_parse_is_code(c99, C99_CODE_OPERATOR_LPAREN))
{
ret |= scan(c99);
if(_parse_in_set(c99, c99set_pointer))
ret |= _pointer(c99);
if(_parse_is_code(c99, C99_CODE_IDENTIFIER))
ret |= _direct_declarator(c99);
else if(_parse_is_code(c99, C99_CODE_OPERATOR_LBRACKET))
ret |= _direct_abstract_declarator(c99);
else if(_parse_in_set(c99, c99set_parameter_type_list))
ret |= _parameter_type_list(c99);
else
/* FIXME get rid of recursivity */
ret |= _abstract_or_declarator(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
else
ret |= _direct_abstract_declarator(c99);
return ret;
}
/* abstract-declarator */
static int _abstract_declarator(C99 * c99)
/* pointer
* [ pointer ] direct-abstract-declarator */
{
int ret = 0;
DEBUG_GRAMMAR();
if(_parse_in_set(c99, c99set_pointer))
ret = _pointer(c99);
if(_parse_in_set(c99, c99set_direct_abstract_declarator))
ret |= _direct_abstract_declarator(c99);
return ret;
}
/* direct-abstract-declarator */
static int _direct_abstract_declarator(C99 * c99)
/* "(" abstract-declarator ")"
* [ direct-abstract-declarator ] "[" [ assignment-expr ] "]"
* direct-abstract-declarator "[" "*" "]"
* [ direct-abstract-declarator ] "(" [ parameter-type-list ] ")" */
{
int ret = 0;
C99Code code;
/* FIXME verify if correct */
DEBUG_GRAMMAR();
code = _parse_get_code(c99);
if(code == C99_CODE_OPERATOR_LPAREN)
{
ret = scan(c99);
if(_parse_in_set(c99, c99set_parameter_type_list))
ret |= _parameter_type_list(c99);
else if(_parse_get_code(c99) != C99_CODE_OPERATOR_RPAREN)
_parse_check_set(c99, c99set_abstract_declarator,
"parameter type list"
"or abstract declarator",
_abstract_declarator);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
else if(code == C99_CODE_OPERATOR_LBRACKET)
{
ret = scan(c99);
if(!_parse_is_code(c99, C99_CODE_OPERATOR_RBRACKET))
ret |= _assignment_expr(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACKET);
}
while((code = _parse_get_code(c99)) != TC_NULL)
if(code == C99_CODE_OPERATOR_LPAREN)
{
ret |= scan(c99);
if(_parse_in_set(c99, c99set_parameter_type_list))
ret |= _parameter_type_list(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
else if(code == C99_CODE_OPERATOR_LBRACKET)
{
if(_parse_is_code(c99, C99_CODE_OPERATOR_TIMES))
ret |= scan(c99);
else if(_parse_in_set(c99, c99set_assignment_expr))
ret |= _assignment_expr(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACKET);
}
else
break;
return ret;
}
/* assignment-expr */
static int _assignment_expr(C99 * c99)
/* { unary-expr assignment-operator } conditional-expr */
{
int ret = 0;
DEBUG_GRAMMAR();
/* FIXME hack around the conflict between unary and conditional */
for(;;)
{
ret |= _conditional_expr(c99);
if(!_parse_in_set(c99, c99set_assignment_operator))
return ret;
ret |= code_context_set(c99->code, CODE_CONTEXT_ASSIGNMENT);
ret |= _assignment_operator(c99);
}
return ret;
}
/* unary-expr */
static int _unary_expr(C99 * c99)
/* FIXME still recursive
* postfix-expr
* "++" unary-expr
* "--" unary-expr
* unary-operator cast-expr
* "sizeof" unary-expr
* "sizeof" "(" type-name ")" */
{
int ret = 0;
C99Code code;
DEBUG_GRAMMAR();
if(_parse_in_set(c99, c99set_postfix_expr))
return _postfix_expr(c99);
/* FIXME use _parse_get_code() */
else if(c99->token == NULL)
return _parse_error(c99, "Unexpected end of file");
else if((code = token_get_code(c99->token)) == C99_CODE_OPERATOR_DPLUS
|| code == C99_CODE_OPERATOR_DMINUS)
{
ret = scan(c99);
ret |= _unary_expr(c99);
}
else if(_parse_in_set(c99, c99set_unary_operator))
{
ret = _unary_operator(c99);
ret |= _cast_expr(c99);
}
else if(code == C99_CODE_KEYWORD_SIZEOF)
{
ret = scan(c99);
if(_parse_is_code(c99, C99_CODE_OPERATOR_LPAREN))
{
ret |= scan(c99);
/* FIXME it may still be an unary-expr ("{") */
if(_parse_in_set(c99, c99set_type_name))
ret |= _type_name(c99);
else
ret |= _parse_check_set(c99, c99set_expression,
"type cast or expression",
_expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
else
ret |= _parse_check_set(c99, c99set_unary_expr,
"type cast or expression",
_unary_expr);
}
return ret;
}
/* postfix-expr
* PRE the first token starts a postfix-expr */
static int _postfix_expr(C99 * c99)
/* primary-expr
* postfix-expr "[" expression "]"
* postfix-expr "(" [ argument-expr-list ] ")"
* postfix-expr "." identifier
* postfix-expr "->" identifier
* postfix-expr "++"
* postfix-expr "--"
* "(" type-name ")" "{" initializer-list [ "," ] "}"
* "(" expression ")" */
{
int ret = 0;
DEBUG_GRAMMAR();
if(_parse_in_set(c99, c99set_primary_expr))
ret |= _primary_expr(c99);
else if(_parse_is_code(c99, C99_CODE_OPERATOR_LPAREN))
{
ret |= scan(c99);
if(_parse_in_set(c99, c99set_type_name))
{
ret |= _type_name(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
ret |= _parse_check(c99, C99_CODE_OPERATOR_LBRACE);
ret |= _initializer_list(c99);
if(_parse_is_code(c99, C99_CODE_COMMA))
ret |= scan(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACE);
}
else if(_parse_in_set(c99, c99set_expression))
{
ret |= _expression(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
else
ret |= _parse_error(c99,
"Expected type name or expression");
}
ret |= _postfix_expr_do(c99);
return ret;
}
/* postfix-expr-do */
static int _postfix_expr_do(C99 * c99)
{
int ret = 0;
C99Code code;
while((code = _parse_get_code(c99)) != TC_NULL)
if(code == C99_CODE_OPERATOR_LBRACKET)
{
ret |= scan(c99);
ret |= _parse_check_set(c99, c99set_expression,
"expression", _expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACKET);
}
else if(code == C99_CODE_OPERATOR_LPAREN)
{
if(code_context_set(c99->code,
CODE_CONTEXT_FUNCTION_PARAMETERS) != 0)
ret |= _parse_error(c99, error_get(NULL));
ret |= scan(c99);
if(!_parse_is_code(c99, C99_CODE_OPERATOR_RPAREN))
ret |= _parse_check_set(c99,
c99set_argument_expr_list,
"argument list",
_argument_expr_list);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
else if(code == C99_CODE_OPERATOR_DOT
|| code == C99_CODE_OPERATOR_MGREATER)
{
ret |= scan(c99);
ret |= _parse_check_set(c99, c99set_identifier,
"identifier", _identifier);
}
else if(code == C99_CODE_OPERATOR_DPLUS
|| code == C99_CODE_OPERATOR_DMINUS)
ret |= scan(c99);
else
break;
return ret;
}
/* argument-expr-list */
static int _argument_expr_list(C99 * c99)
/* assignment-expr { "," assignment-expr } */
{
int ret;
#ifdef DEBUG
unsigned int cnt = 1;
fprintf(stderr, "DEBUG: %s() arg 1 \"%s\"\n", __func__,
_parse_get_string(c99));
#endif
ret = _assignment_expr(c99);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() arg %u \"%s\"\n", __func__, ++cnt,
_parse_get_string(c99));
#endif
ret |= _assignment_expr(c99);
}
return ret;
}
/* primary-expr
* PRE the first token starts a primary-expr */
static int _primary_expr(C99 * c99)
/* identifier
* constant
* string-literal */
{
int ret = 0;
C99Code code;
DEBUG_GRAMMAR();
if(code_context_set(c99->code, CODE_CONTEXT_PRIMARY_EXPR) != 0)
ret |= _parse_error(c99, error_get(NULL));
if((code = token_get_code(c99->token)) == C99_CODE_IDENTIFIER)
{
ret |= _identifier(c99);
if(c99->can_label && _parse_is_code(c99,
C99_CODE_OPERATOR_COLON))
/* handle this as a labeled_statement */
c99->is_label = 1;
}
else if(code == C99_CODE_DQUOTE) /* string-litteral */
{
ret |= scan(c99);
/* FIXME ugly work-around string-literal concatenation */
while(token_get_code(c99->token) == C99_CODE_DQUOTE)
ret |= scan(c99);
}
else /* constant */
ret |= scan(c99);
c99->can_label = 0;
return ret;
}
/* type-name
* PRE the first token starts a type-name */
static int _type_name(C99 * c99)
/* specifier-qualifier-list [ abstract-declarator ] */
{
int ret;
DEBUG_GRAMMAR();
ret = _specifier_qualifier_list(c99);
if(_parse_in_set(c99, c99set_abstract_declarator))
ret |= _abstract_declarator(c99);
return ret;
}
/* specifier-qualifier-list
* PRE the first token starts a specifier-qualifier-list */
static int _specifier_qualifier_list(C99 * c99)
/* (type-specifier | type-qualifier) { (type-specifier | type-qualifier) } */
{
int ret = 0;
int looped = 0;
DEBUG_GRAMMAR();
for(;; looped = 1)
if(_parse_in_set(c99, c99set_type_specifier))
ret |= _type_specifier(c99);
else if(_parse_in_set(c99, c99set_type_qualifier))
ret |= _type_qualifier(c99);
else if(looped == 0)
ret |= _parse_error(c99, "Expected type specifier"
" or type qualifier");
else
break;
return ret;
}
/* unary-operator */
static int _unary_operator(C99 * c99)
/* "&" | "*" | "+" | "-" | "~" | "!" */
{
DEBUG_GRAMMAR();
return scan(c99);
}
/* assignment-operator */
static int _assignment_operator(C99 * c99)
/* "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^="
* | "|=" */
{
DEBUG_GRAMMAR();
return scan(c99);
}
/* conditional-expr */
static int _conditional_expr(C99 * c99)
/* logical-OR-expr { "?" expression ":" logical-OR-expr } */
{
int ret;
DEBUG_GRAMMAR();
ret = _logical_or_expr(c99);
while(_parse_is_code(c99, C99_CODE_OPERATOR_QUESTION))
{
ret |= scan(c99);
ret |= _parse_check_set(c99, c99set_expression, "expression",
_expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_COLON);
ret |= _logical_or_expr(c99);
}
return ret;
}
/* logical-OR-expr */
static int _logical_or_expr(C99 * c99)
/* logical-AND-expr { "||" logical-AND-expr } */
{
int ret;
DEBUG_GRAMMAR();
ret = _logical_and_expr(c99);
while(_parse_is_code(c99, C99_CODE_OPERATOR_DBAR))
{
ret |= scan(c99);
ret |= _logical_and_expr(c99);
}
return ret;
}
/* logical-AND-expr */
static int _logical_and_expr(C99 * c99)
/* inclusive-OR-expr { "&&" inclusive-OR-expr } */
{
int ret;
DEBUG_GRAMMAR();
ret = _inclusive_or_expr(c99);
while(_parse_is_code(c99, C99_CODE_OPERATOR_DAMPERSAND))
{
ret |= scan(c99);
ret |= _inclusive_or_expr(c99);
}
return ret;
}
/* inclusive-OR-expr */
static int _inclusive_or_expr(C99 * c99)
/* exclusive-OR-expr { "|" exclusive-OR-expr } */
{
int ret;
DEBUG_GRAMMAR();
ret = _exclusive_or_expr(c99);
while(_parse_is_code(c99, C99_CODE_OPERATOR_BAR))
{
ret |= scan(c99);
ret |= _exclusive_or_expr(c99);
}
return ret;
}
/* exclusive-OR-expr */
static int _exclusive_or_expr(C99 * c99)
/* AND-expr { "^" AND-expr } */
{
int ret;
DEBUG_GRAMMAR();
ret = _and_expr(c99);
while(_parse_is_code(c99, C99_CODE_OPERATOR_XOR))
{
ret |= scan(c99);
ret |= _and_expr(c99);
}
return ret;
}
/* AND-expr */
static int _and_expr(C99 * c99)
/* equality-expr { "&" equality-expr } */
{
int ret;
DEBUG_GRAMMAR();
ret = _equality_expr(c99);
while(_parse_is_code(c99, C99_CODE_OPERATOR_AMPERSAND))
{
ret |= scan(c99);
ret |= _equality_expr(c99);
}
return ret;
}
/* equality-expr */
static int _equality_expr(C99 * c99)
/* relational-expr { ("==" | "!=") relational-expr } */
{
int ret;
C99Code code;
DEBUG_GRAMMAR();
ret = _relational_expr(c99);
while((code = _parse_get_code(c99)) != TC_NULL
&& (code == C99_CODE_OPERATOR_DEQUALS
|| code == C99_CODE_OPERATOR_NEQUALS))
{
ret |= scan(c99);
ret |= _relational_expr(c99);
}
return ret;
}
/* relational-expr */
static int _relational_expr(C99 * c99)
/* shift-expr { ("<" | ">" | "<=" | ">=") shift-expr } */
{
int ret;
C99Code code;
DEBUG_GRAMMAR();
ret = _shift_expr(c99);
while((code = _parse_get_code(c99)) != TC_NULL
&& (code == C99_CODE_OPERATOR_LESS
|| code == C99_CODE_OPERATOR_GREATER
|| code == C99_CODE_OPERATOR_LEQUALS
|| code == C99_CODE_OPERATOR_GEQUALS))
{
ret |= scan(c99);
ret |= _shift_expr(c99);
}
return ret;
}
/* shift-expr */
static int _shift_expr(C99 * c99)
/* additive-expr { ("<<" | ">>") additive-expr } */
{
int ret;
C99Code code;
DEBUG_GRAMMAR();
ret = _additive_expr(c99);
while((code = _parse_get_code(c99)) != TC_NULL
&& (code == C99_CODE_OPERATOR_DLESS
|| code == C99_CODE_OPERATOR_DGREATER))
{
ret |= scan(c99);
ret |= _additive_expr(c99);
}
return ret;
}
/* additive-expr */
static int _additive_expr(C99 * c99)
/* multiplicative-expr { ("+" | "-") multiplicative-expr } */
{
int ret;
C99Code code;
DEBUG_GRAMMAR();
ret = _multiplicative_expr(c99);
while((code = _parse_get_code(c99)) != TC_NULL
&& (code == C99_CODE_OPERATOR_PLUS
|| code == C99_CODE_OPERATOR_MINUS))
{
ret |= scan(c99);
ret |= _multiplicative_expr(c99);
}
return ret;
}
/* multiplicative-expr */
static int _multiplicative_expr(C99 * c99)
/* cast-expr { ("*" | "/" | "%") cast-expr } */
{
int ret;
C99Code code;
DEBUG_GRAMMAR();
ret = _cast_expr(c99);
while((code = _parse_get_code(c99)) != TC_NULL
&& (code == C99_CODE_OPERATOR_TIMES
|| code == C99_CODE_OPERATOR_DIVIDE
|| code == C99_CODE_OPERATOR_MODULO))
{
ret |= scan(c99);
ret |= _cast_expr(c99);
}
return ret;
}
/* cast-expr */
static int _cast_expr(C99 * c99)
/* { "(" type-name ")" } unary-expr */
{
int ret = 0;
DEBUG_GRAMMAR();
while(_parse_is_code(c99, C99_CODE_OPERATOR_LPAREN))
{
ret |= scan(c99);
/* in both cases it may already be an unary-expr */
if(_parse_in_set(c99, c99set_type_name))
{
ret |= _type_name(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
/* FIXME "{" => postfix-expr with initializer-list */
continue;
}
/* primary-expr */
ret |= _parse_check_set(c99, c99set_expression,
"type cast or expression", _expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
ret |= _postfix_expr_do(c99);
return ret;
}
ret |= _parse_check_set(c99, c99set_unary_expr, "unary expression",
_unary_expr);
return ret;
}
/* compound-statement
* PRE the first token starts a compound-statement */
static int _compound_statement(C99 * c99)
/* "{" [ block-item-list ] "}" */
{
int ret;
DEBUG_GRAMMAR();
ret = scan(c99);
if(code_scope_push(c99->code) < 0)
ret |= _parse_error(c99, error_get(NULL));
if(_parse_in_set(c99, c99set_block_item_list))
ret |= _block_item_list(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACE);
if(code_scope_pop(c99->code) < 0)
ret |= _parse_error(c99, error_get(NULL));
return ret;
}
/* block-item-list
* PRE the first token starts a block-item-list */
static int _block_item_list(C99 * c99)
/* block-item { block-item } */
{
int ret;
DEBUG_GRAMMAR();
ret = _block_item(c99);
while(_parse_in_set(c99, c99set_block_item))
ret |= _block_item(c99);
return ret;
}
/* block-item
* PRE the first token starts a block-item */
static int _block_item(C99 * c99)
/* declaration | statement */
{
DEBUG_GRAMMAR();
if(_parse_in_set(c99, c99set_declaration))
return _declaration(c99);
else if(_parse_in_set(c99, c99set_statement))
return _statement(c99);
/* XXX should be bloat now */
return _parse_error(c99, "Expected declaration or statement");
}
/* statement
* PRE the first token starts a statement */
static int _statement(C99 * c99)
/* labeled-statement
* compound-statement
* expression-statement
* selection-statement
* iteration-statement
* jump-statement */
{
int ret;
DEBUG_GRAMMAR();
ret = code_context_set(c99->code, CODE_CONTEXT_STATEMENT);
if(_parse_in_set(c99, c99set_labeled_statement))
ret |= _labeled_statement(c99);
else if(_parse_in_set(c99, c99set_compound_statement))
ret |= _compound_statement(c99);
else if(_parse_in_set(c99, c99set_expression_statement))
ret |= _expression_statement(c99);
else if(_parse_in_set(c99, c99set_selection_statement))
ret |= _selection_statement(c99);
else if(_parse_in_set(c99, c99set_iteration_statement))
ret |= _iteration_statement(c99);
else if(_parse_in_set(c99, c99set_jump_statement))
ret |= _jump_statement(c99);
return ret;
}
/* labeled-statement
* PRE the first token starts a labeled-statement */
static int _labeled_statement(C99 * c99)
/* identifier ":" statement
* "case" constant-expr ":" statement
* "default" ":" statement */
{
int ret = 0;
C99Code code;
DEBUG_GRAMMAR();
if((code = _parse_get_code(c99)) == C99_CODE_IDENTIFIER)
ret |= _identifier(c99);
else
{
if(!c99->in_switch)
ret |= _parse_error(c99, "Not in a switch");
if(code == C99_CODE_KEYWORD_CASE)
{
ret |= scan(c99);
ret |= _constant_expr(c99);
}
else /* default */
ret |= scan(c99);
}
ret |= _parse_check(c99, C99_CODE_OPERATOR_COLON);
ret |= _parse_check_set(c99, c99set_statement, "statement", _statement);
return ret;
}
/* constant_expr */
static int _constant_expr(C99 * c99)
/* conditional-expr */
{
DEBUG_GRAMMAR();
return _conditional_expr(c99);
}
/* expression-statement
* PRE the first token starts an expression-statement or a label */
static int _expression_statement(C99 * c99)
/* [ expression ] ";"
* identifier ":" */
{
int ret;
DEBUG_GRAMMAR();
if(_parse_is_code(c99, C99_CODE_OPERATOR_SEMICOLON))
return scan(c99);
c99->can_label = 1;
c99->is_label = 0;
ret = _parse_check_set(c99, c99set_expression, "expression",
_expression);
if(c99->is_label)
{
ret |= code_context_set(c99->code, CODE_CONTEXT_LABEL);
ret |= _parse_check(c99, C99_CODE_OPERATOR_COLON);
c99->is_label = 0;
}
else
ret |= _parse_check(c99, C99_CODE_OPERATOR_SEMICOLON);
return ret;
}
/* expression
* PRE the first token starts an expression */
static int _expression(C99 * c99)
/* assignment-expr { "," assignment-expr } */
{
int ret;
DEBUG_GRAMMAR();
ret = _assignment_expr(c99);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
scan(c99);
ret |= _parse_check_set(c99, c99set_assignment_expr,
"assignment expression", _assignment_expr);
}
return ret;
}
/* selection-statement
* PRE the first token starts a selection-statement */
static int _selection_statement(C99 * c99)
/* "if" "(" expression ")" statement [ "else" statement ]
* "switch" "(" expression ")" statement */
{
int ret;
C99Code code;
DEBUG_GRAMMAR();
if((code = _parse_get_code(c99)) == C99_CODE_KEYWORD_SWITCH)
c99->in_switch++;
ret = scan(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_LPAREN);
ret |= _parse_check_set(c99, c99set_expression, "expression",
_expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
ret |= _parse_check_set(c99, c99set_statement, "statement", _statement);
if(code == C99_CODE_KEYWORD_SWITCH)
c99->in_switch--;
else if(code == C99_CODE_KEYWORD_IF
&& _parse_is_code(c99, C99_CODE_KEYWORD_ELSE))
{
ret |= scan(c99);
ret |= _parse_check_set(c99, c99set_statement, "statement",
_statement);
}
return ret;
}
/* iteration-statement
* PRE the first token starts an iteration-statement */
static int _iteration_statement(C99 * c99)
/* while "(" expression ")" statement
* do statement while "(" expression ")" ;
* for "(" [ (expr | declaration) ] ; [ expr ] ; [ expr ] ")" statement */
{
int ret = 0;
C99Code code;
DEBUG_GRAMMAR();
if((code = _parse_get_code(c99)) == C99_CODE_KEYWORD_WHILE)
{
ret = scan(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_LPAREN);
ret |= _parse_check_set(c99, c99set_expression, "expression",
_expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
ret |= _parse_check_set(c99, c99set_statement, "statement",
_statement);
}
else if(code == C99_CODE_KEYWORD_DO)
{
ret = scan(c99);
ret |= _parse_check_set(c99, c99set_statement, "statement",
_statement);
ret |= _parse_check(c99, C99_CODE_KEYWORD_WHILE);
ret |= _parse_check(c99, C99_CODE_OPERATOR_LPAREN);
ret |= _parse_check_set(c99, c99set_expression, "expression",
_expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
}
else if(code == C99_CODE_KEYWORD_FOR)
{
ret = scan(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_LPAREN);
/* FIXME or declaration */
if(!_parse_is_code(c99, C99_CODE_OPERATOR_SEMICOLON))
ret |= _parse_check_set(c99, c99set_expression,
"expression", _expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_SEMICOLON);
if(!_parse_is_code(c99, C99_CODE_OPERATOR_SEMICOLON))
ret |= _parse_check_set(c99, c99set_expression,
"expression", _expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_SEMICOLON);
if(!_parse_is_code(c99, C99_CODE_OPERATOR_RPAREN))
ret |= _parse_check_set(c99, c99set_expression,
"expression", _expression);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RPAREN);
ret |= _parse_check_set(c99, c99set_statement, "statement",
_statement);
}
return ret;
}
/* jump-statement
* PRE the first token starts a jump-statement */
static int _jump_statement(C99 * c99)
/* "goto" identifier ";"
* "continue" ";"
* "break" ";"
* "return" [ expression ] ";" */
{
int ret;
C99Code code;
DEBUG_GRAMMAR();
if((code = token_get_code(c99->token)) == C99_CODE_KEYWORD_GOTO)
{
ret = scan(c99);
ret |= _parse_check_set(c99, c99set_identifier, "identifier",
_identifier);
}
else if(code == C99_CODE_KEYWORD_RETURN)
{
ret = scan(c99);
if(_parse_in_set(c99, c99set_expression))
ret = _expression(c99);
}
else /* continue or break */
ret = scan(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_SEMICOLON);
return ret;
}
/* init-declarator-list */
static int _init_declarator_list(C99 * c99)
/* init-declarator { "," init-declarator } */
{
int ret;
DEBUG_GRAMMAR();
ret = _init_declarator(c99);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
ret |= _init_declarator(c99);
}
return ret;
}
/* init-declarator */
static int _init_declarator(C99 * c99)
/* declarator [ "=" initializer ] */
{
int ret;
DEBUG_GRAMMAR();
ret = _declarator(c99);
if(_parse_is_code(c99, C99_CODE_OPERATOR_EQUALS))
{
ret |= scan(c99);
ret |= _initializer(c99);
}
return ret;
}
/* initializer */
static int _initializer(C99 * c99)
/* assignment-expr
* "{" initializer-list [ "," ] "}" */
{
int ret;
DEBUG_GRAMMAR();
if(_parse_is_code(c99, C99_CODE_OPERATOR_LBRACE))
{
ret = scan(c99);
ret |= _initializer_list(c99);
if(_parse_is_code(c99, C99_CODE_COMMA))
ret |= scan(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACE);
}
else
ret = _assignment_expr(c99);
return ret;
}
/* initializer-list */
static int _initializer_list(C99 * c99)
/* [ designation ] initializer { "," [ designation] initializer } */
{
int ret = 0;
DEBUG_GRAMMAR();
if(_parse_in_set(c99, c99set_designation))
ret |= _designation(c99);
ret |= _initializer(c99);
while(_parse_is_code(c99, C99_CODE_COMMA))
{
ret |= scan(c99);
if(_parse_in_set(c99, c99set_designation))
ret |= _designation(c99);
ret |= _initializer(c99);
}
return ret;
}
/* designation
* PRE the first token starts a designation */
static int _designation(C99 * c99)
/* designator-list "=" */
{
int ret;
DEBUG_GRAMMAR();
ret = _designator_list(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_EQUALS);
return ret;
}
/* designator-list
* PRE the first token starts a designator-list */
static int _designator_list(C99 * c99)
/* designator { designator } */
{
int ret;
DEBUG_GRAMMAR();
ret = _designator(c99);
while(_parse_in_set(c99, c99set_designator))
ret |= _designator(c99);
return ret;
}
/* designator
* PRE the first token starts a designator */
static int _designator(C99 * c99)
/* "[" constant-expression "]"
* "." identifier */
{
int ret;
DEBUG_GRAMMAR();
if(_parse_is_code(c99, C99_CODE_OPERATOR_LBRACKET))
{
ret = scan(c99);
ret |= _constant_expr(c99);
ret |= _parse_check(c99, C99_CODE_OPERATOR_RBRACKET);
}
else
{
ret = scan(c99);
ret |= _parse_check_set(c99, c99set_identifier, "identifier",
_identifier);
}
return ret;
}
/* public */
/* functions */
/* useful */
/* parse */
static int _parse_E(C99 * c99);
int parse(C99 * c99)
{
int ret;
if(c99->outfp != NULL) /* acting like a pre-processor */
return _parse_E(c99);
if((ret = _translation_unit(c99)) != 0)
{
unlink(c99->outfile);
fprintf(stderr, "%s%s%s%u%s%u%s", PACKAGE ": ",
cpp_get_filename(c99->cpp),
": Compilation failed with ", c99->error_cnt,
" error(s) and ", c99->warning_cnt,
" warning(s)\n");
}
return ret;
}
static int _parse_E(C99 * c99)
{
int ret;
Token * token;
C99Code code;
while((ret = cpp_scan(c99->cpp, &token)) == 0 && token != NULL)
{
if((code = token_get_code(token)) == C99_CODE_META_ERROR
|| code == C99_CODE_META_WARNING)
fprintf(stderr, "%s%s%s%s%u%s%s\n",
code == C99_CODE_META_ERROR
? "Error" : "Warning", " in ",
token_get_filename(token), ":",
token_get_line(token), ": ",
token_get_string(token));
else
fputs(token_get_string(token), c99->outfp);
token_delete(token);
}
return ret;
}