1951 lines
46 KiB
C
1951 lines
46 KiB
C
/* $Id$ */
|
|
/* Copyright (c) 2008 Pierre Pronchery <khorben@defora.org> */
|
|
/* This file is part of DeforaOS Devel c99 */
|
|
/* c99 is not free software; you can redistribute it and/or modify it under the
|
|
* terms of the Creative Commons Attribution-NonCommercial-ShareAlike 3.0
|
|
* Unported as published by the Creative Commons organization.
|
|
*
|
|
* c99 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 Creative Commons Attribution-NonCommercial-
|
|
* ShareAlike 3.0 Unported license for more details.
|
|
*
|
|
* You should have received a copy of the Creative Commons Attribution-
|
|
* NonCommercial-ShareAlike 3.0 along with c99; if not, browse to
|
|
* http://creativecommons.org/licenses/by-nc-sa/3.0/ */
|
|
/* 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 "c99.h"
|
|
#include "../config.h"
|
|
|
|
#ifdef DEBUG
|
|
# define DEBUG_GRAMMAR() fprintf(stderr, "DEBUG: %s(%p) \"%s\"\n", __func__, \
|
|
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();
|
|
#if 0
|
|
if(code_function_begin(c99->code, c99->identifier) != 0)
|
|
ret |= _parse_error(c99, error_get());
|
|
free(c99->identifier);
|
|
c99->identifier = NULL;
|
|
ret |= _compound_statement(c99);
|
|
if(code_function_end(c99->code) != 0)
|
|
ret |= _parse_error(c99, error_get());
|
|
#endif
|
|
code_context_set(c99->code, CODE_CONTEXT_FUNCTION_START);
|
|
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 = 0;
|
|
|
|
DEBUG_GRAMMAR();
|
|
for(;; 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());
|
|
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;
|
|
|
|
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());
|
|
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);
|
|
ret |= _parse_check_set(c99, c99set_struct_declaration_list,
|
|
"struct declaration list", _struct_declaration_list);
|
|
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 */
|
|
{
|
|
DEBUG_GRAMMAR();
|
|
return _identifier(c99);
|
|
}
|
|
|
|
|
|
/* 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;
|
|
int 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());
|
|
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))
|
|
return ret | scan(c99);
|
|
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))
|
|
return ret | _direct_declarator(c99);
|
|
/* FIXME there is still an ambiguity with "(" */
|
|
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);
|
|
ret |= _abstract_declarator(c99);
|
|
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 |= _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;
|
|
int 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());
|
|
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;
|
|
int code;
|
|
|
|
DEBUG_GRAMMAR();
|
|
if(code_context_set(c99->code, CODE_CONTEXT_PRIMARY_EXPR) != 0)
|
|
ret |= _parse_error(c99, error_get());
|
|
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 /* constant or string-litteral */
|
|
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;
|
|
int 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;
|
|
int 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;
|
|
int 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;
|
|
int 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());
|
|
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());
|
|
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 */
|
|
{
|
|
DEBUG_GRAMMAR();
|
|
if(_parse_in_set(c99, c99set_labeled_statement))
|
|
return _labeled_statement(c99);
|
|
else if(_parse_in_set(c99, c99set_compound_statement))
|
|
return _compound_statement(c99);
|
|
else if(_parse_in_set(c99, c99set_expression_statement))
|
|
return _expression_statement(c99);
|
|
else if(_parse_in_set(c99, c99set_selection_statement))
|
|
return _selection_statement(c99);
|
|
else if(_parse_in_set(c99, c99set_iteration_statement))
|
|
return _iteration_statement(c99);
|
|
else if(_parse_in_set(c99, c99set_jump_statement))
|
|
return _jump_statement(c99);
|
|
/* XXX should be code bloat now */
|
|
return _parse_error(c99, "Expected statement");
|
|
}
|
|
|
|
|
|
/* 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;
|
|
int 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;
|
|
int 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;
|
|
int 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;
|
|
int code;
|
|
|
|
while((ret = cpp_scan(c99->cpp, &token)) == 0 && token != NULL)
|
|
{
|
|
if((code = token_get_code(token)) == CPP_CODE_META_ERROR
|
|
|| code == CPP_CODE_META_WARNING)
|
|
fprintf(stderr, "%s%s%s%s%u%s%s\n",
|
|
code == CPP_CODE_META_ERROR
|
|
? "Error" : "Warning", " in ",
|
|
token_get_filename(token), ":",
|
|
token_get_line(token), ": ",
|
|
token_get_string(token));
|
|
else if(code >= CPP_CODE_META_FIRST
|
|
&& code <= CPP_CODE_META_LAST)
|
|
fprintf(c99->outfp, "%s\n", token_get_string(token));
|
|
else
|
|
fputs(token_get_string(token), c99->outfp);
|
|
token_delete(token);
|
|
}
|
|
return ret;
|
|
}
|